Post on 14-Apr-2018
transcript
7/27/2019 06 Design Patterns Visitor
1/79
Software Engineering
DesignDesign Patterns: VisitorDesign Patterns: Visitor
Prof. Dr. Mira Mezini
Dipl.-Inform. Christoph BockischDipl.-Ing. Michael Haupt
7/27/2019 06 Design Patterns Visitor
2/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Classification of Design Patterns
Creational Control the way how to create objects
What examples do you know?
Structural
Describe the relation between objects and classes
What examples do you know?
Behavioral
Describe the way how to implement algorithms and communication Has there been an example in the lecture so far?
7/27/2019 06 Design Patterns Visitor
3/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Case-Study: A WYSIWYG Document
Editor
Document represented in the
central rectangular area
Document can mix text andgraphics in a variety of
formatting styles
User can interact by using
menu items
7/27/2019 06 Design Patterns Visitor
4/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Class Diagram of a Documents Elements
children
Document
DocElement
Character Graphic
Row Column
Group
*
Do you recognize
a pattern here?
7/27/2019 06 Design Patterns Visitor
5/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Extend the current design with
Capability to support various algorithms that
Operate on elements of a document In a first prototype, add support for spell
checking
Later we might want to add Word counting
Grammar checking
Hyphenation
Etc...
! Design should be "open" for future extensions
Design Issue: Add Processing
Operations
7/27/2019 06 Design Patterns Visitor
6/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Class Diagram of a Documents Elements
children
Document
DocElement
Character Graphic
Row Column
Group
*
7/27/2019 06 Design Patterns Visitor
7/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Implement spell checking first by adding:
SpellChecker (has a Dictionary and
encapsulates state needed during the process)
Current Design
GroupIterator
Iterator
first() : void
next() : void
current(): DocElementisDone() : boolean
Group
iterator(): Iterator
SpellChecker Dictionary
DocElementchildren
7/27/2019 06 Design Patterns Visitor
8/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Next: add an operation check(SpellChecker) to
DocElements interface
DocElement subclasses implement check in a specific
way
Graphics need not spell checking
Checking a document involves
An iteration over its elements
Calling the specific method for each element
The SpellChecker parameter serves as a workspace
shared by all specific methods that are called during thisprocess
Implementing the check() Function
7/27/2019 06 Design Patterns Visitor
9/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Implementing the check() Function
childrenchildrenDocElementcheck(SpellChecker ch)
Character
check(SpellChecker ch)
Group
check(SpellChecker ch)
Iterator iterator = iterator();iterator.first();while (!iterator.isDone()) {DocElement elem = iterator.current();
elem.check(ch);iterator.next();
}
7/27/2019 06 Design Patterns Visitor
10/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Implementing the check() Function
childrenchildrenDocElementcheck(SpellChecker ch)
Character
check(SpellChecker ch)
Group
check(SpellChecker ch)
char c = getCharCode();if (c.isAlphabetic())
ch.addToCurrentWord(c);else {
if (!dictionary.includes(ch.word()){// handle misspelling
}ch.resetWord();
}
Concatenate current word
When word is done, look it up
7/27/2019 06 Design Patterns Visitor
11/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Achievements Can spell-check documents
Handle different DocElements correctly
Drawbacks DocElement classes need to know about spell checking
To support new algorithms (word counting, grammar checking)
Need to modify DocElement and
Clients ofDocElement classes each time!
DocElement gets more and more complicated, its design is non-cohesive: it captures too many unrelated features.
How can we avoid these problems?
Resume of Current Design
S f T h l
7/27/2019 06 Design Patterns Visitor
12/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Why not encapsulate theprocess of spell checkingcompletely withinSpellChecker?
Encapsulate Spell Checking
SpellChecker
checkGroup( DocElement )
checkCharacter( DocElement )
...
SpellChecker expects the document to bechecked as a parameter; iterates over itselements and checks each individual element.
Software Technology
7/27/2019 06 Design Patterns Visitor
13/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
SpellChecker receives a DocElement in each step ofiteration and needs to distinguish between differentDocElement types
Characters, Graphics, Groups etc. need to behandled differently
in the previous design, inheritance (subtype
polymorphism) ensured that the right checkingoperation was executed for each element
in the new design, this is not possible anymore
What mechanism can we use to distinguish betweenDocElement subclasses?
Determine Element Type
Software Technology
7/27/2019 06 Design Patterns Visitor
14/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Could use the reflective operatorinstanceof in Java or
dynamic casting in C ++
The SpellChecker would check the DocElement for
each subclass that were interested in
Dynamic Casting
Determine Element Type (Dynamic Cast)
Software Technology
7/27/2019 06 Design Patterns Visitor
15/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
Add a className() operation to the DocElement class
(C++) or use RTTI (C++) / Reflection (Java)
Subclasses overload className() to return a string with
the name of the class for clients to test against
if(element.className().equals(CHARACTER_CLASS))
The SpellChecker would retrieve the class for each
DocElement and see how it can handle the specific
object
className() Operation
Determine Element Type (Dynamic Cast)
Software Technology
7/27/2019 06 Design Patterns Visitor
16/79
Software TechnologyGroupTU-Darmstadt | FB Informatik
class SpellChecker {
public void checkGroup(DocElement group) {
Iterator iterator = group.iterator();
iterator.first();while (!iterator.isDone()) {
DocElement elem = iterator.current();
if (elem instanceof Character)checkCharacter(elem);
else if (elem instanceof Graphic)
checkGraphic(elem);
else if (elem instanceof Group)
checkGroup(elem);
iterator.next();
}}} Spell Checking Iteration
Is this good design?Is this good design?
Determine Element Type (Dynamic Cast)
Software Technology
7/27/2019 06 Design Patterns Visitor
17/79
gyGroupTU-Darmstadt | FB Informatik
Solution so far depends on conditional logic to handle
different kinds of document elements
Generally, if there is a design solution that depends on aseries ofif statements or a case statement there is a
more object-oriented way to solve the problem
Lets see if we cant use inheritance in some way to
handle our problem of sorting out the differentsubclasses
Case / If Statements vs. OO
Determine Element Type (Dynamic Cast)
Software Technology
7/27/2019 06 Design Patterns Visitor
18/79
GroupTU-Darmstadt | FB Informatik
Generalize all operations that process documentaggregates to a common type:Visitor
Visitor contains an operation for each element type -
each visitor functionality is split into its element-specificparts
SpellChecker implements theVisitor interface
Adding a Visitor Abstraction
SpellChecker
visit( Group )visit( Character )
...
Visitorvisit( Group )
visit( Character )
...
Software Technology
7/27/2019 06 Design Patterns Visitor
19/79
GroupTU-Darmstadt | FB Informatik
How can the spell checker ensure that the rightvisit() gets called without knowing the dynamictype ofelem?
Iterator iterator =group.iterator();
iterator.first();
while (!iterator.isDone()) {DocElement elem =
iterator.current();visit(elem); ???
iterator.next();}
SpellChecker
visit( Group group )
visit( Character ch )
Visitor: Handling Different Subclasses
let the Java VM do the job forus based on its methodoverloading semantics ...
Software TechnologyG Vi it H dli Diff t S b l
7/27/2019 06 Design Patterns Visitor
20/79
GroupTU-Darmstadt | FB Informatik
Iterator iterator =group.iterator();
iterator.first();
while (!iterator.isDone()) {DocElement elem =
iterator.current();visit(elem); ???
iterator.next();}
Visitor: Handling Different Subclasses
How can the spell checker ensure that the rightvisit() gets called without knowing the dynamictype ofelem?
SpellChecker
visit( Group group )
visit( Character ch )
Compile-Time Error!
Software TechnologyGroup Vi it H dli Diff t S b l
7/27/2019 06 Design Patterns Visitor
21/79
GroupTU-Darmstadt | FB Informatik
Overloaded methods are staticallydispatched. The compiler searchesa method to execute for the callvisit(elem) based on the static
type ofelem.
Since there is no methodvisit(DocElement) in
Visitor's interface anerror will occur.
Iterator iterator =
group.createIterator();
iterator.first();while (!iterator.isDone()) {
DocElement elem =
iterator.currentItem();
visit(elem); ???
iterator.next();
}
Visitor: Handling Different Subclasses
Compile-Time Error!
How can the spell checker ensure that the rightvisit() gets called without knowing the dynamictype ofelem?
Software TechnologyGroup
Vi it H dli Diff t S b l
7/27/2019 06 Design Patterns Visitor
22/79
GroupTU-Darmstadt | FB Informatik Visitor: Handling Different Subclasses
We need a different implementation For each subclass ofVisitor
For each subclass ofDocElement
The concrete types are determined at runtime
The JVM takes the receivers runtime-type into accountwhen selecting a methods implementation
We need to dispatch for runtime-type of two objects Soulution: Double Dispatch
Software TechnologyGroup Visitor Abstraction Adding t()
7/27/2019 06 Design Patterns Visitor
23/79
GroupTU-Darmstadt | FB Informatik
Declare one generic operation in DocElement foraccepting the visit of any processing object rather thanhaving one operation per processing functionality
SpellChecker
visit ( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )...
Visitor Abstraction: Adding accept()
DocElement
checkSpelling( SpellChecker )
checkGrammar(
GrammarChecker )
GrammarChecker
visi t( Group )
visit( Character )
...
DocElement
accept( Visi tor v )
Software TechnologyGroup Visitor Abstraction: Adding t()
7/27/2019 06 Design Patterns Visitor
24/79
GroupTU-Darmstadt | FB Informatik
Concrete DocElementsubclasses implement
accept(Visitor v) by
calling v.visit(this) This ensures that the
specific visit method for the
concrete element class isinvoked
SpellChecker
visit ( Group )
visit( Character )
...
Visitor
visi t( Group )
visit( Character )...
DocElement
accept( Visi tor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
v.visit(this);
Visitor Abstraction: Adding accept()
Software TechnologyGroup Visitor Abstraction: Adding accept()
7/27/2019 06 Design Patterns Visitor
25/79
pTU-Darmstadt | FB Informatik
Iterator it = group.iterator();
it.first();
while (!it.isDone()) {DocElement elem = it.current();
elem.accept( this );
it.next();
}
Visitor Abstraction: Adding accept()
First, dispatch for
DocElements type.
v.visit(this);
Second, dispatch for
Visitors type and call
visit method which takes
Chararcter argument.
v is a SpellChecker and the first element in the group is a Character.
v.visit(group);
DocElement
accept( Visitor v )
Group
accept( Visitor v )
Character
accept( Visitor v )
dynamic
static
SpellChecker
visit ( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
GrammarChecker
visi t( Group )
visit( Character )
...
static
dynamic
Software TechnologyGroup Visitor Abstraction: Adding accept()
7/27/2019 06 Design Patterns Visitor
26/79
TU-Darmstadt | FB Informatik
All accept operations have the sameimplementation. Shouldn't I define
accept(Visitor v) once in DocElementand let subclasses inherit it?
Visitor Abstraction: Adding accept()
DocElement
accept( Visitor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
v.visit(this);
Software TechnologyGroupT | f i Visitor Abstraction: Adding accept()
7/27/2019 06 Design Patterns Visitor
27/79
TU-Darmstadt | FB Informatik Visitor Abstraction: Adding accept()
DocElement
accept( Visi tor v )
Character Group
v.visit(this);
Compile-Time Error!
Again, the compiler uses the static type ofthis to decide
that visit(DocElement) should be called. But there is nosuch operation inVisitor's interface!
Software TechnologyGroupTU Darmstadt | FB Informatik Advantages of the Visitor Abstraction
7/27/2019 06 Design Patterns Visitor
28/79
TU-Darmstadt | FB Informatik
Minimal interface between element types and all kinds ofprocessors (existing and future ones) operating on them
The only thing known to DocElement (and subtypes) is
that they can "accept" the visit of objects that implementVisitor
Concrete type of visitors passedat runtime remains unknown
SpellChecker
visit( Group )visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
GrammarChecker
visit( Group )visit( Character )
...
DocElement
accept( Visitor v )
Advantages of the Visitor Abstraction
Software TechnologyGroupTU-Darmstadt | FB Informatik
Advantages of the Visitor
7/27/2019 06 Design Patterns Visitor
29/79
TU-Darmstadt | FB Informatik
Adding support for hyphenation merely requiresadding a newVisitor class... Existing classes donot change!
Abstraction
SpellChecker
visi t( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
GrammarChecker
visi t( Group )
visit( Character )
...
DocElement
accept( Visitor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
Hyphenator
visit ( Group )
visit( Character )
...
Software TechnologyGroupTU-Darmstadt | FB Informatik The Visitor Pattern
7/27/2019 06 Design Patterns Visitor
30/79
TU Darmstadt | FB Informatik
Represent operations to be performed on theelements of an object structure as an object
New operations can be added without changingthe classes of the elements on which they
operate
The Visitor Pattern
Software TechnologyGroupTU-Darmstadt | FB Informatik
Visitor Pattern Class Diagram
7/27/2019 06 Design Patterns Visitor
31/79
|
ConcreteVisitor1
visit( ElementA )
visit( ElementB )
Visitor
visit( ElementA )
visit( ElementB )
ConcreteVisitor2
visit( ElementA )
visit(ElementB )
Element
accept( Visi tor )
ElementA ElementB
Visitor Pattern Class Diagram
Client
ObjectStructure
accept( Visitor ) accept( Visitor )
*
Alteratives: ObjectStructure
provides iterator or visiting.
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Participants
7/27/2019 06 Design Patterns Visitor
32/79
Visitor declares a "visit" function for each Element
subclass to be visited
an abstract base class for different types ofvisitors
ConcreteVisitor
implements theVisitor abstract interface
provides a concrete implementation for aspecific visiting algorithm
Visitor Pattern Participants
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Participants
7/27/2019 06 Design Patterns Visitor
33/79
Element
abstract base class for various element subclasses
defines an accept() function that takes aVisitoras an argument
ConcreteElement
implements the accept() function and calls the
appropriate Visitor function for this
ConcreteElement
Visitor Pattern Participants
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Participants
7/27/2019 06 Design Patterns Visitor
34/79
ObjectStructure contains multiple Elements (may be a Composite
or any other type of aggregate of elements)
may provide an iterator to iterate through its elementsor may provide a high-level visiting function
Visitor Pattern Participants
Software TechnologyGroupTU-Darmstadt | FB Informatik ObjectStructure/Client Interface
7/27/2019 06 Design Patterns Visitor
35/79
ObjectStructure
iterator()
j
Element
accept(Visitor)
VisitorClient
ConcreteVisitorIterator
ObjectStructure provides an Iterator
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Interaction
7/27/2019 06 Design Patterns Visitor
36/79
iteriter== iteratoriterator()()
anObjectStructureanObjectStructure elementAelementA elementBelementB visitorvisitor
accept(visitor)accept(visitor)visit(elementAvisit(elementA))
accept(visitor)accept(visitor) visit(elementBvisit(elementB))
aClientaClient iteriter
elementAelementA = next()= next()
elementBelementB = next()= next()
External iterator
Software TechnologyGroupTU-Darmstadt | FB Informatik ObjectStructure/Client Interface
7/27/2019 06 Design Patterns Visitor
37/79
j
ObjectStructure
accept(Visitor)
Element
accept(Visitor)
VisitorClient
ConcreteVisitor
*
ObjectStructure provides visiting
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Interaction
7/27/2019 06 Design Patterns Visitor
38/79
accept(visitor)accept(visitor)
anObjectStructureanObjectStructure elementAelementA elementBelementB visitovisito
accept(visitor)accept(visitor)
visit(elementAvisit(elementA))
operationAoperationA()()
accept(visitor)accept(visitor)
visit(elementBvisit(elementB))
operationBoperationB()()
aClientaClient
Internal iterator
Software TechnologyGroupTU-Darmstadt | FB Informatik Visiting Several Class Hierarchies
7/27/2019 06 Design Patterns Visitor
39/79
g
Visitor
visit(ElementA ...)
visit(ElementB...)visit(ElementC ...)
visit(ElementD ...)
BaseElement1
accept(Visitor)
ElementA ElementB
BaseElement2
accept(Visitor)
ElementC ElementD
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Summary of Advantages
7/27/2019 06 Design Patterns Visitor
40/79
+ New operations are easy to add without changingElement classes (just add a new ConcreteVisitor)
+ Related behavior is gathered in a singleConcreteVisitor. Different ConcreteElements
dont have to implement their part of a particular algorithm
+ Allows visiting across hierarchies: visited classes may notshare a common base class
+ Accumulating state: visitors can accumulate state as theyvisit each element, thus encapsulating the algorithm and
all its data within the visitor
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Problems
7/27/2019 06 Design Patterns Visitor
41/79
Adding new ConcreteElements is difficult each time a new ConcreteElement is added that
needs to be visited by a visitor, a function must be added to
the abstractVisitor class and allConcreteVisitors that need to visit the new
ConcreteElement
Software TechnologyGroupTU-Darmstadt | FB Informatik Adding New Elements
7/27/2019 06 Design Patterns Visitor
42/79
What if we dont want to support new elements in existing
visitors?
Sometimes data structures are extended, but its optional to
process extensions
Example: new kind of graphical element (e.g., charts) is added; wedont have to extend spell checking
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
43/79
abstract class DocumentElement {
abstract void accept(Visitor v);
...
}
children
Document
DocElement
Character Graphic
Row Column
Group
*
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
44/79
DocElement
void accept(Visitor v) {
v.visit(this);
}
children
Document
Character Graphic
Row Column
Group
*
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
45/79
SpellChecker
visi t( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
DocElement
accept( Visitor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
46/79
interface Visitor {void visit(Group elem) ;
void visit(Charcter elem);
void visit(Graphics elem);
}
class SpellChecker implements Visitor {String word;
void visit(Group elem) {
foreach(DocumentElement child : elem.childen)
child.accept.this;
}
void visit(Charcter elem) { }
void visit(Graphics elem) { /* ignore */ }
}
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
47/79
SpellChecker
visi t( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
ChartVisitorvisit( Group )
visit( Character )
visit(Chart)
...
DocElement
accept( Visitor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
Chart
accept( Visitor v )
PrintVisitor
visi t( Group )
visit( Character )visit(Chart)
...
void accept(Visitor visitor) {
if(visitor instanceof ChartVisitor)
((ChartVisitor) visitor).visit(this);}
Careful: Chart can only
accept ChartVisitors!
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
48/79
interface ChartVisitor extends Visitor{
void visit(Char elem);
}
class PrintVisitor implements ChartVisitor {void visit(Group elem) { /* decend into group */ }
void visit(Charcter elem) { /* print character */ }
void visit(Graphics elem) { /* print graphics */ }
void visit(Chart elem) { /* print chart */ }
}
interface Visitor {
void visit(Group elem) ;void visit(Charcter elem);
void visit(Graphics elem);
}
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
49/79
SpellChecker
visi t( Group )
visit( Character )
...
Visitor
visit( Group )
visit( Character )
...
ChartVisitorvisit( Group )
visit( Character )
visit(Chart)
...
DocElement
accept( Visitor v )
Character
accept( Visitor v )
Group
accept( Visitor v )
Chart
accept( Visitor v )
ChartSpellChecker
visit ( Group )
visit( Character )visit(Chart)
...
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern Summary
7/27/2019 06 Design Patterns Visitor
50/79
Without the visitor abstraction
adding new functions (algorithms)to an object structure is difficult.
With the visitor abstraction adding
new elements to the objectstructure is difficult.
Software TechnologyGroupTU-Darmstadt | FB Informatik
Two Approaches to Structuring Software
7/27/2019 06 Design Patterns Visitor
51/79
Spaghetti-CodeSpaghetti-Code
Functional DecompositionFunctional Decomposition Object DecompositionObject Decomposition
Form = data typeColor = functionality
Software TechnologyGroupTU-Darmstadt | FB Informatik
Functional Decomposition
7/27/2019 06 Design Patterns Visitor
52/79
Advantagenew functionalityeasy to integrate
Advantagenew functionalityeasy to integrate
DisadvantageIntegrating new data typesrequires the modification
of several functions
DisadvantageIntegrating new data typesrequires the modification
of several functions
Form = data typeColor = functionality
Software TechnologyGroupTU-Darmstadt | FB Informatik
Object Decomposition
7/27/2019 06 Design Patterns Visitor
53/79
AdvantageNew data typeseasy to integrate
AdvantageNew data typeseasy to integrate
Form = data type
Color = functionality
DisadvantageIntegrating new functionalityrequires modification of
several classes
DisadvantageIntegrating new functionalityrequires modification of
several classes
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Adding New Elements
7/27/2019 06 Design Patterns Visitor
54/79
With the visitor pattern, were applying functional
decomposition
Our goal was indeed to facilitate the introduction of
new functionality!
As a by-product we also get the disadvantages offunctional decomposition in our design
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Cyclic Dependencies
7/27/2019 06 Design Patterns Visitor
55/79
Elementaccept(Visitor v)
ElementA
accept(Visitor v)ElementB
accept(Visitor v)
Visitor
visit(ElementA)
visit(ElementB)
Visitor1
visit(ElementA)
visit(ElementB)
Visitor2
visit(ElementA)
visit(ElementB)
Software TechnologyGroupTU-Darmstadt | FB Informatik Visitor Pattern: Cyclic Dependencies
7/27/2019 06 Design Patterns Visitor
56/79
Due to theVisitor-Element dependencycycle of the pattern, even clients ofElementmight need to be recompiled
Adding a new Element type requires to:a) changeVisitor, which in turn requires
b) Element to be recompiled since it depends
uponVisitor, which in turnc) requires clients ofElement to be
recompiled, including all existing derivatives
ofElement and their using clients.
Software TechnologyGroupTU-Darmstadt | FB Informatik
Visitor Pattern: Partial Visiting NotSupported
7/27/2019 06 Design Patterns Visitor
57/79
A method needs to be written for the cross productof allVisitor and Element classes
In order to provide a common abstractVisitor
interface to Element, every derivative ofElementneed to be addressed by every derivative ofVisitor even if this might not make sense or is
not needed
Cannot make use of the polymorphism in theElement hierarchy: accept() must be
implemented in all subclasses
Software TechnologyGroupTU-Darmstadt | FB Informatik
Visitor Pattern: Partial Visiting notSupported
7/27/2019 06 Design Patterns Visitor
58/79
Assume, we have a hierarchy ofModemclasses withone derivative for each manufacturer and we need to
configure modems for different operating systems
We dont want to put configuration functionality inModem, hence, we use the visitor pattern
there is no end to such functions
dont wantModemto recognize any variation on
operating systems
Example
Software TechnologyGroupTU-Darmstadt | FB Informatik
Visitor Pattern: Partial Visiting notSupported
7/27/2019 06 Design Patterns Visitor
59/79
Modem
accept( Visitor v )
HayesModemaccept( Visitor v )
ZoomModem
accept( Visitor v )
Visitor
visit(HayesModem)
visit(ZoomModem)
Configure forUNIX
Configure forDOS
partial visiting not supported
need to implement visit(HayesModem) in the visitor
that performs UNIX configuration, even if we never useHayes modems with UNIX machines
Software TechnologyGroupTU-Darmstadt | FB Informatik
Visitor Pattern: Partial Visiting notSupported
7/27/2019 06 Design Patterns Visitor
60/79
A solution would be to use a hierarchy of visitors
Like in the "Chart Example"
This again couples structure element to kind of
functionality available for it
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism
7/27/2019 06 Design Patterns Visitor
61/79
Querying the intersection of a
shape by another anonymous
shape is common in GUI
programming.
Each concrete shape type has its own definition of
what it means to have an intersection with another
shape
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism
7/27/2019 06 Design Patterns Visitor
62/79
" let intersect(Shape) be a virtual method inShape - the abstract base class of all shapes.
" concrete Shape subclasses must implementintersect(Shape) specifically
" we call this left-handed (singular) polymorphism.
"Object-oriented languagesallow us to implement anintersection operation
specifically for each shapetype in a straightforward way:
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism
7/27/2019 06 Design Patterns Visitor
63/79
...
if (s1.intersect(s2))
...
We get boolean Rectangle.intersect( ??? )
Shape s1 = new Rectangle();
Shape s2 = new Triangle();
Shape s3 = new Circle();
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism
7/27/2019 06 Design Patterns Visitor
64/79
The problem of intersecting two anonymous shapes is agenuine challenge.
Assume that there must be a special intersect()
function for each combination of two shape types.
The operation intersect(Shape) in Shape will only
solve the identity of the left-hand object but will leave the
identity of the right-hand object unsolved.
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
65/79
Double dispatch In addition to the two-shape intersect operation,
intersect(Shape), declare in Shape an operation
for each possible concrete parameter type,intersect(Circle), intersect(Rectangle),
etc.
Redefinitions ofintersect(Shape) in concrete
subclasses ofShape call the specific intersect
function of the parameter object with the receiver asargument.
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
66/79
Circle
Shape
intersect(Shape s)
intersect(Circle c)
intersect(Rectangle r)intersect(Triangle t)
Rectangle Triangle
intersect(Shape s)
intersect(Circle c)
intersect(Rectangle r)intersect(Triangle t)
intersect(Shape s)
intersect(Circle c)
intersect(Rectangle r)intersect(Triangle t)
intersect(Shape s)
intersect(Circle c)
intersect(Rectangle r)
intersect(Triangle t)
// code for deciding whether// a Triangle (receiver)// and a Rectangle (parameter)
// intersect
s.intersect(this);
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
67/79
c
Shape c = new Circle();Shape r = new Rectangle();
c.intersect(r);
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
68/79
r
Shape c = new Circle();Shape r = new Rectangle();
c.intersect(r);
c
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
69/79
r
Shape c = new Circle();Shape r = new Rectangle();
c.intersect(r);
c
intersect(Shape r)
First dispatch: on the dynamic type of the receiverCircle.intersect(Shape) chosen at runtime
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
70/79
r
Shape c = new Circle();Shape r = new Rectangle();
c.intersect(r);
c
Circle.intersect(Shape r) {
r.intersect(this);
}
Compiler knew that this isa Circle. It has changedthe call to: "find a method
for intersect(Circle)based on the dynamic typeofr and call it with c as aparameter"
intersect(Circle c)
Second dispatch: based on the dynamic type of the parameter of
the original call (rhs)Rectangle.intersect(Circle) chosen for execution at runtime
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
71/79
r
Shape c = new Circle();Shape r = new Rectangle();
c.intersect(r);
c
Circle.intersect(Shape r) {r.intersect(this);
}
intersect(Circle c)
Rectangle.intersect(Circle c) {// test whether the Circle c and
// the rectangle "this" intersect }
Finally we get the code that is specific for
the receiver type / parameter type pair.
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Double
Dispatch
7/27/2019 06 Design Patterns Visitor
72/79
The double dispatch approach is efficient and worksin fixed time.
However, it compromises the semantic hierarchyconcept. It results in an inheritance tree where each
derivative is aware of each other's existence.
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Visitor
7/27/2019 06 Design Patterns Visitor
73/79
The cross-reference in each shape derivative toeach other shape derivative may be eliminatedby removing the intersect functions to visitors.
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Visitor
7/27/2019 06 Design Patterns Visitor
74/79
ShapeVisitor v =intersector();
return rhs.accept(v);
Shapeintersect(Shape rhs)
boolean accept(ShapeVisitor v)ShapeVisitor intersector()
Triangle
boolean accept(ShapeVisitor v)ShapeVisitor intersector()
Circle
boolean accept(ShapeVisitor v)ShapeVisitor intersector()
return new CircleIntersect(this);
return new TriangleIntersect(this);
return v.visit(this);
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Visitor
7/27/2019 06 Design Patterns Visitor
75/79
ShapeVisitor
boolean visit (Circle)boolean visit (Triangle)
CircleIntersect
boolean visit (Circle)boolean visit (Triangle)
TriangleIntersect
boolean visit (Circle)boolean visit (Triangle)
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Visitor
7/27/2019 06 Design Patterns Visitor
76/79
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Multi-
Methods
7/27/2019 06 Design Patterns Visitor
77/79
Multi-methods: Manage a registry ofintersect() functions
taking two shapes.
Each function is registered with two class
identifiers.
Given two class identifiers, the general
intersect() function looks up the handler in theregistry.
Software TechnologyGroupTU-Darmstadt | FB Informatik
Double Polymorphism with Multi-Methods
7/27/2019 06 Design Patterns Visitor
78/79
Circle Rectangle
Shape
intersect(Shape s)
...
Triangle
static multiMethCircle/Triangle
Circle/
Rectangle
Multi-methods
String otherClass = s.getClass().getName();
String thisClass = getClass().getName();
Method meth = (Method) multiMeth.elementAt(thisClass, otherClass);
Object[] args = new Object[1];args[0] = s;
Object res = meth.invoke(this, args);
boolean result = ((Boolean) res)getBooleanValue();
return result
Software TechnologyGroupTU-Darmstadt | FB Informatik Double Polymorphism with Multi-
Methods
7/27/2019 06 Design Patterns Visitor
79/79
The multi-method solution is expensive.
It also involves extensive application of down casting
There are object-oriented programming languages that
directly support multiple dispatch
CLOS (dynamically typed, LISP derivative),
CECIL (statically typed),
MultiJava (statically typed, Java dialect)
Will use MultiJava in lab later this term.