Design Patterns & Refactoring
Visitor
Oliver Haase
HTWG Konstanz
Description
Classification: Object-based behavioral pattern
Purpose: Separate algorithm from the object structure upon which it
operates.
Motivation
interfac e Lis t + sum(): int + chars(): String Client
Nil + sum(): int + chars(): String
IntElement - head: int - tail: List + sum(): int + chars(): String
Element - tail: List + sum(): int + chars(): String
CharElement - head: char - tail: List + sum(): int + chars(): String
Motivation
p u b l i c i n t e r f a c e L i s t { i n t sum ( ) ;
S t r i n g c h a r s ( ) ; }
p u b l i c c l a s s N i l implements L i s t { p u b l i c S t r i n g c h a r s ( ) { r e t u r n ” ” ; } p u b l i c i n t sum ( ) { r e t u r n 0 ; } }
p u b l i c a b s t r a c t c l a s s E l e m e n t implements L i s t { p r o t e c t e d L i s t t a i l ;
p r o t e c t e d E l e m e n t ( L i s t t a i l ) { t h i s. t a i l = t a i l ; } }
Motivation
p u b l i c c l a s s I n t E l e m e n t e x t e n d s E l e m e n t { p r i v a t e i n t h e a d ;
p u b l i c I n t E l e m e n t (i n t number , L i s t t a i l ) { s u p e r( t a i l ) ;
h e a d = number ; }
p u b l i c S t r i n g c h a r s ( ) { r e t u r n t a i l . c h a r s ( ) ; } p u b l i c i n t sum ( ) { r e t u r n h e a d + t a i l . sum ( ) ; } }
p u b l i c c l a s s C h a r E l e m e n t e x t e n d s E l e m e n t { p r i v a t e c h a r h e a d ;
p u b l i c C h a r E l e m e n t (c h a r c h a r a c t e r , L i s t t a i l ) { s u p e r( t a i l ) ;
h e a d = c h a r a c t e r ; }
p u b l i c S t r i n g c h a r s ( ) { r e t u r n h e a d + t a i l . c h a r s ( ) ; } p u b l i c i n t sum ( ) { r e t u r n t a i l . sum ( ) ; }
}
Motivation
p u b l i c c l a s s C l i e n t {
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
L i s t l = new I n t E l e m e n t ( 4 , new C h a r E l e m e n t ( ’ b ’ , new C h a r E l e m e n t ( ’ a ’ , new I n t E l e m e n t ( 3 ,
new N i l ( ) ) ) ) ) ;
Syst em . o u t . p r i n t l n ( ”Summe : ” + l . sum ( ) ) ; Syst em . o u t . p r i n t l n ( ”Summe : ” + l . c h a r s ( ) ) ; }
}
Motivation
Problem: Introduction of a new operation, e.g. charCount(), requires
modification of List interface IntElement class CharElement class
Generally, of all classes of the object structure.
Motivation
Problem: Introduction of a new operation, e.g. charCount(), requires modification of
List interface IntElement class CharElement class
Generally, of all classes of the object structure.
Key Idea
Idea: Separate operations (sum(), chars, . . . ) into visitor classes that traverse the object structure.
⇓
For each specific visitor, provide one visit operation per element type.
To visit an element, call its accept operation which in turn calls the
appropriate visit operation.
Sample Structure
interfac e Lis t + accept(Visitor): void Client
Nil
+ accept(Visitor): void
IntElement - head: int - tail: List
+ accept(Visitor): void Element - tail: List
+ accept(Visitor): void
CharElement - head: char - tail: List
+ accept(Visitor): void inte rface Vis itor
+ vis it(Nil): void + vis it(IntElement): void + vis it(CharElement): void
S umVis itor + vis it(Nil): void + vis it(IntElement): void + vis it(CharElement): void
Chars Vis itor + vis it(Nil): void + vis it(IntElement): void + vis it(CharElement): void
Sample Implementation
p u b l i c i n t e r f a c e L i s t {
v o i d a c c e p t ( V i s i t o r v i s i t o r ) ; }
p u b l i c c l a s s N i l implements L i s t { p u b l i c v o i d a c c e p t ( V i s i t o r v i s i t o r ) {
v i s i t o r . v i s i t (t h i s) ; }
}
p u b l i c a b s t r a c t c l a s s E l e m e n t implements L i s t { p r o t e c t e d L i s t t a i l ;
p r o t e c t e d E l e m e n t ( L i s t t a i l ) { t h i s. t a i l = t a i l ;
}
p u b l i c L i s t g e t T a i l ( ) { r e t u r n t a i l ; } }
Sample Implementation
p u b l i c c l a s s I n t E l e m e n t e x t e n d s E l e m e n t { p r i v a t e i n t h e a d ;
p u b l i c I n t E l e m e n t (i n t number , L i s t t a i l ) { s u p e r( t a i l ) ;
h e a d = number ; }
p u b l i c i n t g e t H e a d ( ) { r e t u r n h e a d ; } p u b l i c v o i d a c c e p t ( V i s i t o r v i s i t o r ) {
v i s i t o r . v i s i t (t h i s) ; }
}
p u b l i c i n t e r f a c e V i s i t o r { v o i d v i s i t ( N i l l i s t ) ;
v o i d v i s i t ( I n t E l e m e n t l i s t ) ; v o i d v i s i t ( C h a r E l e m e n t l i s t ) ; }
Sample Implementation
p u b l i c c l a s s S u m V i s i t o r implements V i s i t o r { p r i v a t e i n t sum = 0 ;
p u b l i c v o i d v i s i t ( N i l l i s t ) {}
p u b l i c v o i d v i s i t ( I n t E l e m e n t l i s t ) { sum += l i s t . g e t H e a d ( ) ;
l i s t . g e t T a i l ( ) . a c c e p t (t h i s) ; }
p u b l i c v o i d v i s i t ( C h a r E l e m e n t l i s t ) { l i s t . g e t T a i l ( ) . a c c e p t (t h i s) ;
}
p u b l i c i n t getSum ( ) { r e t u r n sum ; } }
Please note: Visitor can (and sometimes must) store state information.
Sample Implementation
p u b l i c c l a s s C l i e n t {
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
L i s t l = new I n t E l e m e n t ( 4 , new C h a r E l e m e n t ( ’ b ’ , new C h a r E l e m e n t ( ’ a ’ , new I n t E l e m e n t ( 3 ,
new N i l ( ) ) ) ) ) ;
S u m V i s i t o r s v = new S u m V i s i t o r ( ) ; l . a c c e p t ( s v ) ;
Syst em . o u t . p r i n t l n ( ”Summe : ” + s v . getSum ( ) ) ; C h a r s V i s i t o r c v = new C h a r s V i s i t o r ( ) ;
l . a c c e p t ( c v ) ;
Syst em . o u t . p r i n t l n ( ”Summe : ” + c v . g e t C h a r s ( ) ) ; }
}
General Structure
interfac e Element + accept(Visitor): void Client
Conc reteElementA + accept(Visitor): void
inte rface Vis itor
+ vis it(ConcreteElementA): void + vis it(ConcreteElementB): void
Conc reteElementB + accept(Visitor): void
Concre te Vis itor
+ vis it(ConcreteElementA): void + vis it(ConcreteElementB): void
Please note: Concrete elements need not have a common supertype.
Participants
Visitor : declares an overloaded visit operation for each concrete element type
ConreteVisitor :
provide implementation for each overloadedvisitoperation stores state information
provides operation to retrieve final state
Element: defines an accept operation with a Visitor as argument
ConcreteElement: implements the accept operation, usually by
calling the visitor’s appropriate visit operation.
Consequences of Modifications
modify operation modify object structure w/o visitor
pattern
adapt all structural classes
adapt only affected structural class
with visitor pattern
adapt only affected visitor
adapt all visitor classes
Visitor pattern is beneficial if object structure is more stable than
operations on it.
Traversal Logic
There are two main variants of the visitor pattern, depending on which entities contain the logic for the traversal of the object structure:
1
Visitors (see example)
+ know the desired traversal order
− structural information is duplicated (object structure + all visitors)
2
Elements
+ natural place to keep structural information confined
− don’t know the desired traversal order
Advanced Considerations
The interaction sequence
Conc reteVis itor Conc reteElement accept(this)
visit(this)
seems a little awkward. It is only necessary, because most OO languages
(including Java and C#) support only single dispatch rather than double
dispatch.
Advanced Considerations
If the visitor inspects the runtime type of an element, it can directly call its own appropriate visit operations without the indirection via the
element’s accept operation:
p u b l i c a b s t r a c t c l a s s V i s i t o r {
f i n a l p u b l i c v o i d v i s i t ( L i s t l i s t ) { i f ( l i s t i n s t a n c e o f N i l ) {
v i s i t ( ( N i l ) l i s t ) ; }
i f ( l i s t i n s t a n c e o f I n t E l e m e n t ) { v i s i t ( ( I n t E l e m e n t ) l i s t ) ;
}
i f ( l i s t i n s t a n c e o f C h a r E l e m e n t ) { v i s i t ( ( C h a r E l e m e n t ) l i s t ) ;
} }
a b s t r a c t p u b l i c v o i d v i s i t ( N i l l i s t ) ;
a b s t r a c t p u b l i c v o i d v i s i t ( I n t E l e m e n t l i s t ) ;
Advanced Considerations
p u b l i c c l a s s S u m V i s i t o r e x t e n d s V i s i t o r { p r i v a t e i n t sum = 0 ;
p u b l i c v o i d v i s i t ( N i l l i s t ) {}
p u b l i c v o i d v i s i t ( I n t E l e m e n t l i s t ) { sum += l i s t . g e t H e a d ( ) ;
v i s i t ( l i s t . g e t T a i l ( ) ) ; }
p u b l i c v o i d v i s i t ( C h a r E l e m e n t l i s t ) { v i s i t ( l i s t . g e t T a i l ( ) ) ;
}
p u b l i c i n t getSum ( ) { r e t u r n sum ; } }
Advanced Considerations
p u b l i c i n t e r f a c e L i s t {}
p u b l i c c l a s s N i l implements L i s t {}
p u b l i c a b s t r a c t c l a s s E l e m e n t implements L i s t { p r o t e c t e d L i s t t a i l ;
p r o t e c t e d E l e m e n t ( L i s t t a i l ) { t h i s. t a i l = t a i l ;
}
p u b l i c L i s t g e t T a i l ( ) { r e t u r n t a i l ; } }
Advanced Considerations
p u b l i c c l a s s I n t E l e m e n t e x t e n d s E l e m e n t { p r i v a t e i n t h e a d ;
p u b l i c I n t E l e m e n t (i n t number , L i s t t a i l ) { s u p e r( t a i l ) ;
h e a d = number ; }
p u b l i c i n t g e t H e a d ( ) { r e t u r n h e a d ; } }
Advanced Considerations
p u b l i c c l a s s C l i e n t {
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
L i s t l = new I n t E l e m e n t ( 4 , new C h a r E l e m e n t ( ’ b ’ ,
new C h a r E l e m e n t ( ’ a ’ , new I n t E l e m e n t ( 3 , new N i l ( ) ) ) ) ) ; S u m V i s i t o r s v = new S u m V i s i t o r ( ) ;
s v . v i s i t ( l ) ;
Syste m . o u t . p r i n t l n ( ”Summe : ” + s v . getSum ( ) ) ; C h a r s V i s i t o r c v = new C h a r s V i s i t o r ( ) ;
c v . v i s i t ( l ) ;
Syste m . o u t . p r i n t l n ( ”Summe : ” + c v . g e t C h a r s ( ) ) ; }
}
Even More Advanced Consideration
With introspection, the Visitor base class can do without the switch:
p u b l i c a b s t r a c t c l a s s V i s i t o r {
f i n a l p u b l i c v o i d v i s i t ( L i s t l i s t ) { O b j e c t [ ] o s = {l i s t};
C l a s s<?>[] c s = {l i s t . g e t C l a s s ( )}; t r y {
t h i s. g e t C l a s s ( ) . g e t M e t h o d ( ” v i s i t ” , c s ) . i n v o k e (t h i s , o s ) ; }
c a t c h ( E x c e p t i o n e ) { . . . } }
a b s t r a c t p u b l i c v o i d v i s i t ( N i l l i s t ) ;
a b s t r a c t p u b l i c v o i d v i s i t ( I n t E l e m e n t l i s t ) ; a b s t r a c t p u b l i c v o i d v i s i t ( C h a r E l e m e n t l i s t ) ; }