780 likes | 909 Views
Unit 28 Visitor. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 29 Visitor. Summary prepared by Kirk Scott. The Introduction Before the Introduction. My presentation of the visitor pattern will follow this outline:
E N D
Unit 28Visitor Summary prepared by Kirk Scott
Design Patterns in JavaChapter 29Visitor Summary prepared by Kirk Scott
The Introduction Before the Introduction • My presentation of the visitor pattern will follow this outline: • The book presents the Visitor design pattern by using it to extend code which is based on the Composite design pattern • I will follow this example in order to define the pattern
I will then try to illustrate the pattern with an example simpler than the book’s • In my example the Visitor pattern will be applied to a leaf of a Component only • I will not give complete code for an implementation of the pattern for a Composite
I will then return to the book’s discussion of an important syntactical point that becomes apparent with this pattern • I will then examine the potential shortcomings of the pattern
The posted “assignment” for this unit deals with an implementation of the pattern for a Composite even though I don’t pursue that in depth in these overheads • It should be within your ability to apply the pattern in that context, using the book and these overheads as a guide • Such a question could appear on the last test
Starting the Book Material on the Pattern • Assuming that you have access to the code for a class, extending the class would mean adding methods to it, for example • If you, as a client developer, cannot change the code of a given class, the class developer can make it possible for you to add functionality to the class
The developer of the service class defines a visitor interface containing visit() methods • The developer of the service class defines a an accept() method in the service classes • The accept() method accepts an instance of a visitor as an explicit parameter • Inside the accept() method, visit() is called on the service object that accept() was called on
The client developer creates concrete classes that implement the visitor interface • These classes contain concrete implementations of the visit() method • It is these concrete visit() methods that the service code accept() method calls on itself
The client code creates a service object, creates a visitor object, and calls accept() on the service object, passing the visitor in • Note how passing objects that contain desired functionality is reminiscent of the Command design pattern
Book Definition of Pattern • Book definition: • The intent of Visitor is to let you define a new operation for a hierarchy without changing the hierarchy classes.
Visitor Mechanics • The visitor pattern is based on some standard mechanics • These mechanics potentially support an unlimited variety of extensions (new methods) that can be applied to service classes by clients • The needed mechanics fall into three parts:
The Visitor Interface • 1. There is a visitor interface that is associated with the set of service classes • This visitor interface is defined to have one or more methods named visit() • There will be a different version of the visit() method for each service class • Each visit() method takes as its explicit parameter an instance of the service class that it applies to
The Concrete Client Classes that Implement the Visitor Interface • 2. The client side will develop concrete classes that implement the interface • The concrete classes implement the versions of the visit() method for the service classes • These concrete implementations of the method embody the new functionality that is to be added to/passed to the hierarchy of classes • Keep in mind that the visit() methods take in service class objects as their explicit parameters
The accept() Method in the Service Classes • 3. Each class in the service hierarchy has to have an accept() method • The accept() method takes an explicit parameter that is typed to the visitor interface • When the accept() method is called on a service class object, a visitor object is passed in
Inside the accept() method, the visit() method is called on the explicit parameter that was passed in • The explicit parameter that is sent to the visit() method is the service class object object that accept() was called on
An Example with a Composite • The UML diagram given on the overhead following the next one shows the MachineComponent composite hierarchy with visitation added • The MachineComponent composite hierarchy is the service class hierarchy • MachineComponent has an (abstract) accept() method • Machine and MachineComposite have concrete accept() methods
The UML diagram also shows the MachineVisitor interface • This interface contains two visit() methods • One visit() method takes a Machine object as its explicit parameter • The other visit() method takes a MachineComposite object as its explicit parameter
Fleshing Out MachineVisitor • If client code is going to make use of visitation, it has to create concrete visitor classes • These concrete visitor classes contain concrete implementations of the visit() methods • The concrete visit() methods contain the functionality that is being “added to” the existing service hierarchy • The functionality being added in this example is the ability to “find” a component in a composite
The UML diagram on the overhead following the next one shows a concrete class, FindVisitor • It implements the MachinedVisitor interface • An instance of FindVisitor would be the object passed as an explicit parameter when calling the accept() method on a Machine or MachineComposite object
FindVisitor has two versions of visit() • One version takes an instance of Machine as its explicit parameter • The other takes an instance of MachineComposite as its explicit parameter • FindVisitor also has an application specific method, find() • How find() works depends on how visit() is coded
Another Example • In order to follow the book’s example you would have to follow the logic of traversing the composite and implementing the logic of finding • I don’t want to deal with those complexities • Therefore, the “other” example comes right away • This is the example I want to pursue
The logic of this example is obtaining the name of a machine in reverse order • This is simpler than implementing the logic of finding in a composite • Also, I only do the implementation for a leaf • That way I avoid even having to consider traversing the composite • The focus is on the Visitor pattern, not on the complexities of the Composite pattern
Machine Names • Suppose that the Machine class has a String instance variable name and a getName() method • Suppose that the class doesn’t have a getNameReversed() method, which would return the String name in reverse order, but this functionality is desired
Suppose that you don’t have access to the Machine class in order to implement this • Suppose also that this is something that you might want to do more than once • You don’t just want to call getName() in client code and then reverse the name every time the need arises in client code • Instead, you’d like to package up the functionality in a visitor
Code will be given for a NameReversedVisitor class for getting the names of machine components in reversed order • The class will contain two visit() methods, one for Machine and one for MachineComposite • These methods actually contain the logic for reversing names • Only the code for the Machine version will be given
The Calling Sequence • The NameReversedVisitor class will contain a descriptively named method, getNameReversed() • Client code will construct an instance of NameReversedVisitor • Client code will call getNameReversed() on that visitor • Client code will pass a MachineComponent object as an explicit parameter to getNameReversed()
getNameReversed() calls accept() on the MachineComponent parameter (in the leaf case, a simple Machine) • The version of accept() for a Machine will be called • The accept() method calls visit() on the visitor and passes in “this” the machine that accept() was called on, as an implicit parameter
This verbal description is kind of hopelessly twisted • Hopefully, it will be possible to follow the code • The code for the NameReversedVisitor class is given on the next overhead • Remember that an implementation of visit() is only given for the Machine class
public class NameReversedVisitor implements MachineVisitor • { • public String getNameReversed(MachineComponent mc) • { • mc.accept(this); • } • public String visit(Machine m) • { • String name = m.getName(); • String retString = ""; • for(inti = name.length() - 1; i >= 0; i--) • { • retString = retString + name.substring(i, i + 1); • } • return retString; • } • public String visit(MachineCompositemachineCompositeIn) • { • // Some other implementation. • } • }
This is what accept() looks like: • public void accept(MachineVisitor v) • { • v.visit(this); • }
The Calling Sequence, Repeated • The code execution sequence would go like this: • Client code constructs a machine visitor and a machine • It calls the getNameReversed() method on the visitor, passing in the machine • The implementation of getNameReversed() consists of a call to the accept() method on the machine, passing in the visitor
The implementation of accept() consists of a call to the visit() method on the visitor, passing in the machine • The visit() method contains logic that gets the name of the machine and returns a string containing the name in reversed order
I have provided no separate UML diagram for the other example • It would be just like the book’s UML diagrams with the FindVisitor replaced by the ReversedNameVisitor • The book’s example is going to be pursued in greater detail next, so the diagrams aren’t repeated here • They will be shown again shortly
A Critical Aspect of the Implementation of the Pattern • This design pattern illustrates a subtle aspect of Java syntax that hasn’t come up before • It has to do with where methods are declared and defined, and whether or not it is practical to inherit them • It has specifically to do with the declaration and implementation of the visit() method and the call to visit() in the accept() method
The MachineVisitor Interface • The UML diagram for the Machine Component hierarchy and the MachineVisitor interface is shown again on the following overhead • There are separate visit() methods, one that takes a Machine as its parameter, another that takes a MachineComposite as its parameter • The visit() method declarations in the interface are shown on the overhead following the next one
public interface MachineVisitor • { • void visit(Machine m); • void visit(MachineComposite mc); • }
The accept() Method in the Service Classes (Composite Hierarchy) • This was breezed over before, but the UML diagram has a mistake in it • The accept(:MachineVisitor) method in the MachineComponent class should have been in italics, because this is the abstract method • Its implementation is forced into the concrete subclasses Machine and MachineComposite
Now the book makes the following observation: • In each of the subclasses, Machine and MachineComposite, the implementation of the accept() method is going to look exactly the same: • public void accept(MachineVisitor v) • { • v.visit(this); • }
When you see this, you might think that the implementation of accept() can be pushed into the superclass (as implied by the mistake in the UML diagram) • In other words, you might think that there is no need for it to be abstract in the MachineComponent class • This is not the case
The compiler will see a difference between the implementations of the methods in the superclass and the two subclasses • It all depends on the meaning of the term “this”, depending on the context in which it appears