Unit 28 Visitor

78
Unit 28 Visitor Summary prepared by Kirk Scott 1

description

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: - PowerPoint PPT Presentation

Transcript of Unit 28 Visitor

Page 1: Unit 28 Visitor

1

Unit 28Visitor

Summary prepared by Kirk Scott

Page 2: Unit 28 Visitor

2

Page 3: Unit 28 Visitor

3

Page 4: Unit 28 Visitor

4

Page 5: Unit 28 Visitor

5

Page 6: Unit 28 Visitor

6

Page 7: Unit 28 Visitor

7

Page 8: Unit 28 Visitor

8

Design Patterns in JavaChapter 29

Visitor

Summary prepared by Kirk Scott

Page 9: Unit 28 Visitor

9

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

Page 10: Unit 28 Visitor

10

• 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

Page 11: Unit 28 Visitor

11

• 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

Page 12: Unit 28 Visitor

12

• 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

Page 13: Unit 28 Visitor

13

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

Page 14: Unit 28 Visitor

14

• 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

Page 15: Unit 28 Visitor

15

• 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

Page 16: Unit 28 Visitor

16

• 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

Page 17: Unit 28 Visitor

17

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.

Page 18: Unit 28 Visitor

18

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:

Page 19: Unit 28 Visitor

19

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

Page 20: Unit 28 Visitor

20

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

Page 21: Unit 28 Visitor

21

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

Page 22: Unit 28 Visitor

22

• 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

Page 23: Unit 28 Visitor

23

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

Page 24: Unit 28 Visitor

24

• 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

Page 25: Unit 28 Visitor

25

Page 26: Unit 28 Visitor

26

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

Page 27: Unit 28 Visitor

27

• 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

Page 28: Unit 28 Visitor

28

• 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

Page 29: Unit 28 Visitor

29

Page 30: Unit 28 Visitor

30

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

Page 31: Unit 28 Visitor

31

• 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

Page 32: Unit 28 Visitor

32

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

Page 33: Unit 28 Visitor

33

• 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

Page 34: Unit 28 Visitor

34

• 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

Page 35: Unit 28 Visitor

35

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()

Page 36: Unit 28 Visitor

36

• 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

Page 37: Unit 28 Visitor

37

• 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

Page 38: Unit 28 Visitor

38

• 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(int i = name.length() - 1; i >= 0; i--)• {• retString = retString + name.substring(i, i + 1);• }• return retString;• }• public String visit(MachineComposite machineCompositeIn)• {• // Some other implementation.• }• }

Page 39: Unit 28 Visitor

39

• This is what accept() looks like:

• public void accept(MachineVisitor v)• {• v.visit(this);• }

Page 40: Unit 28 Visitor

40

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

Page 41: Unit 28 Visitor

41

• 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

Page 42: Unit 28 Visitor

42

• 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

Page 43: Unit 28 Visitor

43

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

Page 44: Unit 28 Visitor

44

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

Page 45: Unit 28 Visitor

45

Page 46: Unit 28 Visitor

46

• public interface MachineVisitor• {• void visit(Machine m);• void visit(MachineComposite mc);• }

Page 47: Unit 28 Visitor

47

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

Page 48: Unit 28 Visitor

48

• 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);• }

Page 49: Unit 28 Visitor

49

• 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

Page 50: Unit 28 Visitor

50

• 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

Page 51: Unit 28 Visitor

51

• Challenge 29.1• “What difference will a Java compiler see

between the accept() methods in the Machine and MachineComposite() classes?

• (Don’t peek on this one unless you have to; it’s key to understanding Visitor.)”

Page 52: Unit 28 Visitor

52

• Solution 29.1• “The difference is in the type of the this object. • The accept() method calls the visit() method of a

MachineVisitor object. • The accept() method in the Machine class will look

up a visit() method with the signature visit(:Machine), whereas the accept() method in the MachineComposite class will look up a method with the signature visit(:MachineComposite).”

Page 53: Unit 28 Visitor

53

• Comment mode on:• By studying the given code closely, it is possible to

answer the challenge correctly even without fully grasping what it means

• “this” is the only thing that could change between the two implementations of the method

• To tell the truth, at first glance I wasn’t completely convinced, so I looked into the alternative that the book rejected

Page 54: Unit 28 Visitor

54

• What would happen if you did implement the accept() method in MachineComponent and let the two subclasses inherit it?

• Not only will the compiler see the difference in the correct solution

• It will tell you something is wrong with the incorrect solution

Page 55: Unit 28 Visitor

55

• This is what’s in the MachineVisitor interface:

• public interface MachineVisitor• {• void visit(Machine m);• void visit(MachineComposite mc);• }

Page 56: Unit 28 Visitor

56

• The accept() method would look exactly the same if you defined it in the abstract MachineComponent superclass and the concrete subclasses inherited it:

• public void accept(MachineVisitor v)• {• v.visit(this);• }

Page 57: Unit 28 Visitor

57

What’s Wrong

• The compiler will tell you that it can’t find a version of the visit() method that takes an instance of MachineComponent as the explicit parameter

• In the MachineVisitor class there are only versions of visit() that take an instance of Machine or an instance of MachineComposite

Page 58: Unit 28 Visitor

58

An Elaboration of What’s Wrong

• We have seen cases in the past where you can call a method with an explicit parameter typed to a superclass, and it’s OK to pass in an actual parameter object that is of a subclass

• In a sense, this is the reverse of that situation• You are trying to call a method with an explicit

parameter of a superclass type, when the only implementations of the method have subclass formal parameters

Page 59: Unit 28 Visitor

59

• You can’t pass a superclass parameter to something that is typed to accept a subclass

• That is, you can’t have a subclass reference to a superclass type, (unless it’s a situation where you can check the reference type with instanceof and recover the subclass reference by casting)

Page 60: Unit 28 Visitor

60

• Consider this “reverse” case of superclass/subclass parameter passing again

• It’s really a question about overloading• You have various versions of the method that

are distinguished by their parameter types

Page 61: Unit 28 Visitor

61

• Syntactically, the problem is that there isn’t a version with a superclass parameter

• Logically, there shouldn’t be one—so all is well• This just means that you have to push the

implementations into the subclasses, as shown

Page 62: Unit 28 Visitor

62

Potential Shortcomings of the Pattern

• This design pattern has certain shortcomings• The UML diagram showing a concrete visitor

class is repeated on the following overhead for reference

Page 63: Unit 28 Visitor

63

Page 64: Unit 28 Visitor

64

• This is the motivating idea:• The machine composite hierarchy doesn’t

implement a find() method• The desire arises to have one, without

implementing it in the machine composite hierarchy

• This can be done by means of a visitor

Page 65: Unit 28 Visitor

65

• The first thing you might observe about the pattern is that it’s kind of round-about

• The visit() and accept() methods are essentially intertwined

Page 66: Unit 28 Visitor

66

• The service classes have accept() methods• Those accept() methods take instances of visitor

objects• The visitor objects have versions of the visit()

method with a parameter for each of the service classes

• For a given service object, inside the body of accept(), visit() is called on the visitor, passing in “this” as the explicit parameter

Page 67: Unit 28 Visitor

67

• This intertwinement is unavoidable when you are trying to meet these conditions:

• You can’t change the service code class• You still want to package up the functionality

so that it can be used more than once

Page 68: Unit 28 Visitor

68

Is This a Good Idea?

• The next consideration is whether this is a good idea at all

• In order to implement find() in the book’s example it is necessary to traverse a composite data structure using visit()

• The underlying operation is to find a given node in the structure, but different versions of visit() are needed for machines and machine composites

Page 69: Unit 28 Visitor

69

• This example illustrates the fundamental shortcoming of the design pattern

• As an outsider, without access to the service class’s internals, how do you know how to correctly traverse the structures?

• Do you know, for example, whether cycles are allowed, and whether you’ll have to handle them?

• Remember the discussion of isTree() when presenting the Composite pattern

Page 70: Unit 28 Visitor

70

• If traversal is so important, why wasn’t it built into the class?

• If there are potential pitfalls, shouldn’t the code be implemented in the service class and not externally?

• Why can’t it be added to the class now?• Why has the correct handling of a potentially

problematic situation suddenly become the responsibility of someone who is writing client code?

Page 71: Unit 28 Visitor

71

• There are no ultimate answers to these questions

• Some people think the pattern is useful• Others think it’s not• In the long run, I think it’s worth covering not

because of its intent, but because of the point made at the beginning about Java syntax and compilation

Page 72: Unit 28 Visitor

72

• It is important to keep careful track of how the compiler tells things apart

• This can affect where in an inheritance hierarchy you implement methods that look the same

• It was not a trick, but really, a special case• The methods looked the same because they both

referred to “this”, but “this” in two different classes will be two different kinds of objects

Page 73: Unit 28 Visitor

73

Lasater’s UML

• Lasater’s diagram, this time, does not seem as informative as the book’s diagrams

• It includes a client, but it shows closed arrowheads from client to visitor and from client to object structure to element

• I’m pretty sure he was daydreaming and these should be open arrowheads

• It is apparent that there are accept() and visit() methods, but otherwise the diagram is not very helpful

Page 74: Unit 28 Visitor

74

Page 75: Unit 28 Visitor

75

One Last Remark at the End of the Composite Based Patterns—Don’t Skip This

• The visitor pattern is the last of the patterns related to composite as presented in the overheads for the units in the course

• This is just a last remark having to do with composites

• You may recall the unit on the Iterator pattern

Page 76: Unit 28 Visitor

76

• The book chapter on the Iterator came after the chapter on composites

• This meant that the book could cover iteration over a composite and I did not do so in the unit on iteration

• In theory it would be possible to have one last section, in order to cover iteration over a composite now

Page 77: Unit 28 Visitor

77

• This would make for a pretty comprehensive treatment of composites

• I will not cover it in these overheads and there will be no extra unit on this

• The main point of mentioning this is that you should still have some grasp of iteration when taking the last test

• There is a possibility that iteration will come again in the context of composites

Page 78: Unit 28 Visitor

78

The End