Design Pattern When You Dont Know What Type of Object
In object-oriented programming and software engineering, the visitor pattern pattern is a way of separating an algorithm from an object structure on which information technology operates. A applied result of this separation is the ability to add together new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle.
In essence, the company allows adding new virtual functions to a family of classes, without modifying the classes. Instead, a visitor class is created that implements all of the appropriate specializations of the virtual part. The company takes the example reference as input, and implements the goal through double dispatch.
Overview [edit]
The Visitor [i] design design is one of the twenty-iii well-known GoF pattern patterns that describe how to solve recurring design issues to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.
What problems can the Company design pattern solve? [edit]
- It should be possible to define a new operation for (some) classes of an object structure without changing the classes.
When new operations are needed often and the object structure consists of many unrelated classes, it'south inflexible to add together new subclasses each time a new functioning is required because "[..] distributing all these operations across the various node classes leads to a system that's hard to understand, maintain, and alter."[1]
What solution does the Visitor design design depict? [edit]
- Ascertain a separate (visitor) object that implements an performance to exist performed on elements of an object structure.
- Clients traverse the object structure and phone call a dispatching operation take (company) on an element — that "dispatches" (delegates) the request to the "accustomed visitor object". The company object then performs the operation on the element ("visits the chemical element").
This makes information technology possible to create new operations independently from the classes of an object structure by adding new visitor objects.
See too the UML form and sequence diagram beneath.
Definition [edit]
The Gang of 4 defines the Company as:
Represent[ing] an operation to be performed on elements of an object construction. Visitor lets you define a new functioning without changing the classes of the elements on which it operates.
The nature of the Company makes it an platonic design to plug into public APIs, thus allowing its clients to perform operations on a class using a "visiting" form without having to modify the source.[2]
Advantages [edit]
Moving operations into company classes is beneficial when
- many unrelated operations on an object structure are required,
- the classes that make up the object structure are known and not expected to change,
- new operations need to be added frequently,
- an algorithm involves several classes of the object structure, just it is desired to manage it in i single location,
- an algorithm needs to work across several independent class hierarchies.
A drawback to this pattern, still, is that it makes extensions to the course hierarchy more than difficult, as new classes typically crave a new visit
method to be added to each company.
Awarding [edit]
Consider the pattern of a 2nd computer-aided design (CAD) organisation. At its core, there are several types to represent basic geometric shapes similar circles, lines, and arcs. The entities are ordered into layers, and at the summit of the blazon hierarchy is the drawing, which is simply a listing of layers, plus some added properties.
A fundamental operation on this type hierarchy is saving a drawing to the system's native file format. At first glance, information technology may seem acceptable to add local salve methods to all types in the hierarchy. But it is also useful to be able to save drawings to other file formats. Adding always more methods for saving into many dissimilar file formats shortly clutters the relatively pure original geometric data structure.
A naive way to solve this would be to maintain split up functions for each file format. Such a save function would take a drawing as input, traverse it, and encode into that specific file format. As this is done for each added different format, duplication between the functions accumulates. For example, saving a circle shape in a raster format requires very similar code no matter what specific raster form is used, and is different from other primitive shapes. The case for other primitive shapes like lines and polygons is similar. Thus, the code becomes a large outer loop traversing through the objects, with a large determination tree inside the loop querying the type of the object. Another problem with this approach is that it is very easy to miss a shape in one or more savers, or a new archaic shape is introduced, but the save routine is implemented just for one file type and not others, leading to code extension and maintenance problems. As the versions of the same file grows it becomes more complicated to maintain it.
Instead, the visitor blueprint tin be practical. It encodes a logical operation on the whole hierarchy into i class containing one method per type. In the CAD example, each save function would be implemented equally a separate Visitor subclass. This would remove all duplication of type checks and traversal steps. It would as well make the compiler mutter if a shape is omitted.
Iteration loops [edit]
Visitor pattern may be used for iteration over container-similar information structures just like Iterator pattern but with limited functionality.[3] : 288 For example, iteration over a directory construction could be implemented past a function grade instead of more conventional loop pattern. This would let deriving diverse useful data from directories content by implementing a visitor functionality for every item while reusing the iteration code. It'south widely employed in Smalltalk systems and can exist found in C++ as well.[iii] : 289 A drawback of this approach, however, is that you can't break out of the loop easily or iterate concurrently (in parallel i.due east. traversing two containers at the same fourth dimension by a single i
variable).[3] : 289 The latter would crave writing additional functionality for a visitor to back up these features.[3] : 289
Structure [edit]
UML class and sequence diagram [edit]
In the UML course diagram above, the ElementA
class doesn't implement a new performance directly. Instead, ElementA
implements a dispatching operation have(visitor)
that "dispatches" (delegates) a request to the "accepted visitor object" (visitor.visitElementA(this)
). The Visitor1
class implements the functioning (visitElementA(e:ElementA)
).
ElementB
so implements accept(visitor)
by dispatching to visitor.visitElementB(this)
. The Visitor1
grade implements the performance (visitElementB(e:ElementB)
).
The UML sequence diagram shows the run-time interactions: The Customer
object traverses the elements of an object construction (ElementA,ElementB
) and calls take(visitor)
on each element.
First, the Customer
calls have(visitor)
on ElementA
, which calls visitElementA(this)
on the accepted visitor
object. The element itself (this
) is passed to the visitor
so that it can "visit" ElementA
(call operationA()
).
Thereafter, the Client
calls accept(visitor)
on ElementB
, which calls visitElementB(this)
on the visitor
that "visits" ElementB
(calls operationB()
).
Form diagram [edit]
Details [edit]
The company blueprint requires a programming linguistic communication that supports single dispatch, as common object-oriented languages (such equally C++, Java, Smalltalk, Objective-C, Swift, JavaScript, Python and C#) practise. Under this status, consider ii objects, each of some class type; one is termed the element, and the other is visitor.
The company declares a visit
method, which takes the element as an argument, for each form of element. Concrete visitors are derived from the visitor grade and implement these visit
methods, each of which implements part of the algorithm operating on the object structure. The land of the algorithm is maintained locally by the concrete visitor class.
The element declares an accept
method to accept a visitor, taking the visitor as an statement. Physical elements, derived from the element class, implement the have
method. In its simplest course, this is no more than a call to the visitor's visit
method. Composite elements, which maintain a list of kid objects, typically iterate over these, calling each kid'due south accept
method.
The client creates the object construction, directly or indirectly, and instantiates the concrete visitors. When an operation is to exist performed which is implemented using the Visitor design, it calls the accept
method of the elevation-level chemical element(south).
When the accept
method is called in the plan, its implementation is chosen based on both the dynamic type of the element and the static type of the visitor. When the associated visit
method is called, its implementation is chosen based on both the dynamic type of the visitor and the static blazon of the element, every bit known from within the implementation of the accept
method, which is the same as the dynamic type of the element. (Every bit a bonus, if the visitor tin can't handle an argument of the given chemical element'southward blazon, then the compiler will grab the error.)
Thus, the implementation of the visit
method is called based on both the dynamic type of the element and the dynamic type of the visitor. This effectively implements double acceleration. For languages whose object systems support multiple dispatch, not only unmarried dispatch, such equally Common Lisp or C# via the Dynamic Linguistic communication Runtime (DLR), implementation of the visitor pattern is profoundly simplified (a.k.a. Dynamic Visitor) by allowing apply of elementary function overloading to encompass all the cases being visited. A dynamic company, provided it operates on public information but, conforms to the open/airtight principle (since information technology does not modify extant structures) and to the single responsibility principle (since it implements the Visitor design in a separate component).
In this manner, 1 algorithm tin can exist written to traverse a graph of elements, and many different kinds of operations can exist performed during that traversal by supplying different kinds of visitors to interact with the elements based on the dynamic types of both the elements and the visitors.
C# example [edit]
This case declares a separate ExpressionPrintingVisitor
grade that takes intendance of the printing.
namespace Wikipedia ; public form ExpressionPrintingVisitor { public void PrintLiteral ( Literal literal ) { Panel . WriteLine ( literal . Value ); } public void PrintAddition ( Addition addition ) { double leftValue = improver . Left . GetValue (); double rightValue = addition . Correct . GetValue (); var sum = addition . GetValue (); Console . WriteLine ( "{0} + {1} = {2}" , leftValue , rightValue , sum ); } } public abstract class Expression { public abstract void Accept ( ExpressionPrintingVisitor five ); public abstract double GetValue (); } public form Literal : Expression { public double Value { get ; set ; } public Literal ( double value ) { this . Value = value ; } public override void Accept ( ExpressionPrintingVisitor v ) { v . PrintLiteral ( this ); } public override double GetValue () { render Value ; } } public form Addition : Expression { public Expression Left { get ; ready ; } public Expression Right { get ; set ; } public Add-on ( Expression left , Expression right ) { Left = left ; Correct = right ; } public override void Accept ( ExpressionPrintingVisitor v ) { Left . Accept ( v ); Right . Accept ( v ); v . PrintAddition ( this ); } public override double GetValue () { return Left . GetValue () + Correct . GetValue (); } } public static class Program { public static void Chief ( string [] args ) { // Emulate 1 + 2 + 3 var east = new Addition ( new Addition ( new Literal ( 1 ), new Literal ( 2 ) ), new Literal ( iii ) ); var printingVisitor = new ExpressionPrintingVisitor (); e . Take ( printingVisitor ); } }
Smalltalk example [edit]
In this case, it is the object'due south responsibleness to know how to print itself on a stream. The visitor here is and then the object, non the stream.
"There's no syntax for creating a class. Classes are created by sending letters to other classes." WriteStream subclass: #ExpressionPrinter instanceVariableNames: '' classVariableNames: '' package: 'Wikipedia' . ExpressionPrinter >>write: anObject "Delegates the activity to the object. The object doesn't need to exist of any special class; it just needs to be able to understand the message #putOn:" anObject putOn: cocky . ^ anObject . Object subclass: #Expression instanceVariableNames: '' classVariableNames: '' package: 'Wikipedia' . Expression subclass: #Literal instanceVariableNames: 'value' classVariableNames: '' package: 'Wikipedia' . Literal class>>with: aValue "Class method for building an example of the Literal class" ^ self new value: aValue ; yourself . Literal >>value: aValue "Setter for value" value := aValue . Literal >>putOn: aStream "A Literal object knows how to print itself" aStream nextPutAll: value asString . Expression bracket: #Addition instanceVariableNames: 'left right' classVariableNames: '' package: 'Wikipedia' . Addition class>>left: a right: b "Class method for building an instance of the Add-on class" ^ cocky new left: a ; right: b ; yourself . Add-on >>left: anExpression "Setter for left" left := anExpression . Addition >>right: anExpression "Setter for right" right := anExpression . Addition >>putOn: aStream "An Addition object knows how to print itself" aStream nextPut: $( . left putOn: aStream . aStream nextPut: $+ . right putOn: aStream . aStream nextPut: $) . Object subclass: #Plan instanceVariableNames: '' classVariableNames: '' package: 'Wikipedia' . Programme >> main | expression stream | expression := Improver left: (Addition left: (Literal with: 1) right: (Literal with: 2)) right: (Literal with: 3). stream := ExpressionPrinter on: (Cord new: 100). stream write: expression . Transcript bear witness: stream contents . Transcript flush .
Become [edit]
Go does not back up overloading, so the visit methods demand different names. A typical company interface might be
type Visitor interface { visitWheel ( bike Wheel ) string visitEngine ( engine Engine ) string visitBody ( body Torso ) cord visitCar ( automobile Machine ) string }
Java instance [edit]
The following example is in the language Java, and shows how the contents of a tree of nodes (in this case describing the components of a auto) can be printed. Instead of creating print
methods for each node subclass (Wheel
, Engine
, Body
, and Car
), one visitor class (CarElementPrintVisitor
) performs the required printing action. Because different node subclasses crave slightly dissimilar deportment to impress properly, CarElementPrintVisitor
dispatches deportment based on the class of the statement passed to its visit
method. CarElementDoVisitor
, which is coordinating to a save functioning for a different file format, does too.
Diagram [edit]
Sources [edit]
import java.util.Listing ; interface CarElement { void have ( CarElementVisitor visitor ); } interface CarElementVisitor { void visit ( Trunk trunk ); void visit ( Car motorcar ); void visit ( Engine engine ); void visit ( Wheel wheel ); } course Bike implements CarElement { individual final String name ; public Wheel ( final String name ) { this . name = proper noun ; } public Cord getName () { render proper name ; } @Override public void accept ( CarElementVisitor company ) { /* * have(CarElementVisitor) in Wheel implements * accept(CarElementVisitor) in CarElement, then the call * to accept is bound at run time. This can be considered * the *first* dispatch. However, the decision to phone call * visit(Wheel) (as opposed to visit(Engine) etc.) can exist * made during compile fourth dimension since 'this' is known at compile * time to be a Wheel. Moreover, each implementation of * CarElementVisitor implements the visit(Wheel), which is * another determination that is fabricated at run time. This can be * considered the *second* acceleration. */ visitor . visit ( this ); } } class Body implements CarElement { @Override public void accept ( CarElementVisitor company ) { visitor . visit ( this ); } } class Engine implements CarElement { @Override public void have ( CarElementVisitor visitor ) { visitor . visit ( this ); } } form Car implements CarElement { private final Listing < CarElement > elements ; public Motorcar () { this . elements = List . of ( new Bicycle ( "front left" ), new Bicycle ( "forepart right" ), new Cycle ( "dorsum left" ), new Wheel ( "back correct" ), new Trunk (), new Engine () ); } @Override public void accept ( CarElementVisitor visitor ) { for ( CarElement element : elements ) { element . accept ( visitor ); } visitor . visit ( this ); } } class CarElementDoVisitor implements CarElementVisitor { @Override public void visit ( Body body ) { Organisation . out . println ( "Moving my torso" ); } @Override public void visit ( Automobile car ) { System . out . println ( "Starting my car" ); } @Override public void visit ( Wheel wheel ) { System . out . println ( "Boot my " + cycle . getName () + " bike" ); } @Override public void visit ( Engine engine ) { System . out . println ( "Starting my engine" ); } } class CarElementPrintVisitor implements CarElementVisitor { @Override public void visit ( Trunk body ) { System . out . println ( "Visiting body" ); } @Override public void visit ( Automobile car ) { Organization . out . println ( "Visiting car" ); } @Override public void visit ( Engine engine ) { System . out . println ( "Visiting engine" ); } @Override public void visit ( Bike wheel ) { Organisation . out . println ( "Visiting " + wheel . getName () + " cycle" ); } } public class VisitorDemo { public static void main ( final String [] args ) { Car automobile = new Automobile (); car . accept ( new CarElementPrintVisitor ()); car . take ( new CarElementDoVisitor ()); } }
Output [edit]
Visiting forepart left wheel Visiting front end right bike Visiting dorsum left bicycle Visiting back right wheel Visiting body Visiting engine Visiting car Kicking my front left wheel Kicking my front right cycle Boot my back left bike Kicking my back right cycle Moving my torso Starting my engine Starting my car
Common Lisp example [edit]
Sources [edit]
( defclass motorcar () (( elements :initarg :elements ))) ( defclass car-part () (( name :initarg :name :initform "<unnamed-car-office>" ))) ( defmethod impress-object (( p auto-part ) stream ) ( print-object ( slot-value p 'name ) stream )) ( defclass wheel ( automobile-part ) ()) ( defclass trunk ( auto-role ) ()) ( defclass engine ( auto-part ) ()) ( defgeneric traverse ( function object other-object )) ( defmethod traverse ( office ( a auto ) other-object ) ( with-slots ( elements ) a ( dolist ( eastward elements ) ( funcall function e other-object )))) ;; practice-something visitations ;; catch all ( defmethod do-something ( object other-object ) ( format t "don't know how ~s and ~s should interact~%" object other-object )) ;; visitation involving bicycle and integer ( defmethod practice-something (( object wheel ) ( other-object integer )) ( format t "kicking cycle ~due south ~s times~%" object other-object )) ;; visitation involving wheel and symbol ( defmethod do-something (( object wheel ) ( other-object symbol )) ( format t "boot wheel ~s symbolically using symbol ~southward~%" object other-object )) ( defmethod do-something (( object engine ) ( other-object integer )) ( format t "starting engine ~southward ~s times~%" object other-object )) ( defmethod do-something (( object engine ) ( other-object symbol )) ( format t "starting engine ~s symbolically using symbol ~south~%" object other-object )) ( allow (( a ( make-case 'auto :elements ` ( , ( make-case 'bike :proper name "front-left-wheel" ) , ( make-instance 'wheel :proper name "front end-right-wheel" ) , ( brand-instance 'bicycle :name "rear-left-wheel" ) , ( brand-instance 'wheel :name "rear-right-wheel" ) , ( make-instance 'body :name "body" ) , ( brand-instance 'engine :proper name "engine" ))))) ;; traverse to print elements ;; stream *standard-output* plays the role of other-object hither ( traverse #' impress a *standard-output* ) ( terpri ) ;; print newline ;; traverse with arbitrary context from other object ( traverse #' practise-something a 42 ) ;; traverse with arbitrary context from other object ( traverse #' do-something a 'abc ))
Output [edit]
"forepart-left-bike" "forepart-right-cycle" "rear-left-wheel" "rear-right-wheel" "body" "engine" kicking bicycle "front end-left-wheel" 42 times boot wheel "front end-correct-wheel" 42 times kicking bicycle "rear-left-cycle" 42 times kicking cycle "rear-right-wheel" 42 times don't know how "body" and 42 should interact starting engine "engine" 42 times kicking wheel "forepart-left-wheel" symbolically using symbol ABC boot bike "front-correct-wheel" symbolically using symbol ABC kicking wheel "rear-left-wheel" symbolically using symbol ABC boot bike "rear-right-wheel" symbolically using symbol ABC don't know how "body" and ABC should collaborate starting engine "engine" symbolically using symbol ABC
Notes [edit]
The other-object
parameter is superfluous in traverse
. The reason is that it is possible to utilize an anonymous function that calls the desired target method with a lexically captured object:
( defmethod traverse ( function ( a machine )) ;; other-object removed ( with-slots ( elements ) a ( dolist ( e elements ) ( funcall function e )))) ;; from here too ;; ... ;; alternative style to print-traverse ( traverse ( lambda ( o ) ( print o *standard-output* )) a ) ;; alternative manner to do-something with ;; elements of a and integer 42 ( traverse ( lambda ( o ) ( practice-something o 42 )) a )
Now, the multiple dispatch occurs in the call issued from the body of the bearding role, and then traverse
is just a mapping function that distributes a function application over the elements of an object. Thus all traces of the Visitor Pattern disappear, except for the mapping role, in which there is no evidence of ii objects being involved. All knowledge of there being two objects and a dispatch on their types is in the lambda function.
Python example [edit]
Python does non support method overloading in the classical sense (polymorphic behavior according to type of passed parameters), and so the "visit" methods for the unlike model types need to accept different names.
Sources [edit]
""" Visitor design example. """ from abc import ABCMeta , abstractmethod NOT_IMPLEMENTED = "You should implement this." class CarElement : __metaclass__ = ABCMeta @abstractmethod def accept ( self , visitor ): raise NotImplementedError ( NOT_IMPLEMENTED ) course Torso ( CarElement ): def take ( self , visitor ): visitor . visitBody ( self ) grade Engine ( CarElement ): def accept ( self , visitor ): company . visitEngine ( self ) grade Wheel ( CarElement ): def __init__ ( self , proper noun ): self . proper noun = name def accept ( self , company ): visitor . visitWheel ( self ) class Machine ( CarElement ): def __init__ ( self ): self . elements = [ Cycle ( "front left" ), Wheel ( "front right" ), Wheel ( "dorsum left" ), Wheel ( "dorsum correct" ), Torso (), Engine () ] def take ( self , visitor ): for element in cocky . elements : element . accept ( company ) visitor . visitCar ( self ) class CarElementVisitor : __metaclass__ = ABCMeta @abstractmethod def visitBody ( cocky , element ): heighten NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def visitEngine ( cocky , element ): heighten NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def visitWheel ( cocky , chemical element ): raise NotImplementedError ( NOT_IMPLEMENTED ) @abstractmethod def visitCar ( self , element ): heighten NotImplementedError ( NOT_IMPLEMENTED ) grade CarElementDoVisitor ( CarElementVisitor ): def visitBody ( self , body ): print ( "Moving my body." ) def visitCar ( self , car ): print ( "Starting my car." ) def visitWheel ( self , wheel ): print ( "Kicking my {} wheel." . format ( wheel . name )) def visitEngine ( self , engine ): impress ( "Starting my engine." ) class CarElementPrintVisitor ( CarElementVisitor ): def visitBody ( self , body ): print ( "Visiting body." ) def visitCar ( cocky , car ): print ( "Visiting car." ) def visitWheel ( self , wheel ): impress ( "Visiting {} wheel." . format ( bike . name )) def visitEngine ( self , engine ): print ( "Visiting engine." ) car = Car () auto . accept ( CarElementPrintVisitor ()) auto . accept ( CarElementDoVisitor ())
Output [edit]
Visiting front left wheel. Visiting front right wheel. Visiting back left wheel. Visiting dorsum right wheel. Visiting body. Visiting engine. Visiting automobile. Kicking my front end left wheel. Kick my front right wheel. Kicking my back left wheel. Kick my dorsum right wheel. Moving my body. Starting my engine. Starting my machine.
Brainchild [edit]
If one is using Python 3 or to a higher place, they can make a general implementation of the have method:
course Visitable : def accept ( cocky , visitor ): lookup = "visit_" + type ( self ) . __qualname__ . replace ( "." , "_" ) return getattr ( company , lookup )( self )
One could extend this to iterate over the class's method resolution order if they would like to fall back on already-implemented classes. They could also utilize the subclass hook feature to define the lookup in advance.
[edit]
- Iterator pattern – defines a traversal principle like the company pattern, without making a type differentiation inside the traversed objects
- Church encoding – a related concept from functional programming, in which tagged union/sum types may be modeled using the behaviors of "visitors" on such types, and which enables the visitor pattern to emulate variants and patterns.
See also [edit]
- Algebraic data blazon
- Double acceleration
- Multiple dispatch
- Role object
References [edit]
- ^ a b Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Pattern Patterns: Elements of Reusable Object-Oriented Software . Addison Wesley. pp. 331ff. ISBN0-201-63361-2.
{{cite book}}
: CS1 maint: multiple names: authors list (link) - ^ Visitor pattern real-world instance
- ^ a b c d Budd, Timothy (1997). An introduction to object-oriented programming (2d ed.). Reading, Mass.: Addison-Wesley. ISBN0-201-82419-one. OCLC 34788238.
- ^ "The Company pattern pattern - Structure and Collaboration". w3sDesign.com . Retrieved 2017-08-12 .
- ^ Reddy, Martin (2011). API design for C++. Boston: Morgan Kaufmann. ISBN978-0-12-385004-1. OCLC 704559821.
External links [edit]
- The Visitor Family unit of Design Patterns at the Wayback Machine (archived October 22, 2015). Additional archives: April 12, 2004, March 5, 2002. A rough chapter from The Principles, Patterns, and Practices of Agile Software Development, Robert C. Martin, Prentice Hall
- Visitor pattern in UML and in LePUS3 (a Blueprint Description Language)
- Article "Componentization: the Visitor Example by Bertrand Meyer and Karine Arnout, Computer (IEEE), vol. 39, no. 7, July 2006, pages 23-30.
- Commodity A Blazon-theoretic Reconstruction of the Visitor Pattern
- Article "The Essence of the Visitor Pattern" by Jens Palsberg and C. Barry Jay. 1997 IEEE-CS COMPSAC paper showing that take() methods are unnecessary when reflection is bachelor; introduces term 'Walkabout' for the technique.
- Commodity "A Time for Reflection" by Bruce Wallace – subtitled "Java 1.2's reflection capabilities eliminate burdensome accept() methods from your Visitor design"
- Visitor Design using reflection(java).
- PerfectJPattern Open Source Project, Provides a context-free and type-safe implementation of the Visitor Pattern in Coffee based on Delegates.
- Visitor Design Pattern
Design Pattern When You Dont Know What Type of Object
Source: https://en.wikipedia.org/wiki/Visitor_pattern
0 Response to "Design Pattern When You Dont Know What Type of Object"
Post a Comment