A visitor pattern is a software design pattern that separates the algorithm from the object structure. Because of this separation, new operations can be added to existing object structures without modifying the structures. It is one way to follow the open/closed principle in object-oriented programming and software engineering.
In essence, the visitor 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 function. The visitor takes the instance reference as input, and implements the goal through double dispatch.
Programming languages with sum types and pattern matching obviate many of the benefits of the visitor pattern, as the visitor class is able to both easily branch on the type of the object and generate a compiler error if a new object type is defined which the visitor does not yet handle.
The Visitor[1] design pattern is one of the twenty-three Gang of Four design patterns.
When new operations are needed frequently and the object structure consists of many unrelated classes, it's inflexible to add new subclasses each time a new operation is required because "distributing all these operations across the various node classes leads to a system that's hard to understand, maintain, and change."[1]
This makes it possible to create new operations independently from the classes of an object structure by adding new visitor objects.
See also the UML class and sequence diagram below.
The Gang of Four defines the Visitor as:
Represent[ing] an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
ビジターの性質上、公開APIに組み込むのに理想的なパターンであり、クライアントはソースコードを変更することなく、「ビジティング」クラスを使用してクラスに対する操作を実行できます。[ 2 ]
業務をビジタークラスに移管すると、次のような場合に有益です。
しかし、このパターンには欠点があり、新しいクラスを追加するには、通常、各ビジターに新しいメソッドを追加する必要があるため、クラス階層の拡張が難しくなるvisit。
2次元コンピュータ支援設計(CAD)システムの設計を考えてみましょう。その中核には、円、線、円弧といった基本的な幾何学的形状を表すためのいくつかのタイプがあります。これらのエンティティはレイヤーに整理され、タイプの階層の最上位には図面があります。図面は、単にレイヤーのリストにいくつかの追加プロパティを加えたものです。
この型階層における基本的な操作は、図面をシステムのネイティブファイル形式で保存することです。一見すると、階層内のすべての型にローカル保存メソッドを追加するのが望ましいように思えるかもしれません。しかし、図面を他のファイル形式で保存することも有用です。さまざまなファイル形式への保存メソッドを次々と追加していくと、元の幾何データ構造が複雑化する可能性があります。
この問題を解決する単純な方法は、ファイル形式ごとに個別の関数を用意することです。このような保存関数は、入力として図面を受け取り、それを走査して、特定のファイル形式にエンコードします。この処理をファイル形式ごとに繰り返すと、関数間の重複が蓄積されます。たとえば、円形をラスター形式で保存する場合、使用するラスター形式に関係なく、非常に似たコードが必要となり、他のプリミティブ形状とは異なります。線や多角形などの他のプリミティブ形状の場合も同様です。そのため、コードはオブジェクトを走査する大きな外側のループと、ループ内部でオブジェクトのタイプを照会する大きな決定木で構成されます。このアプローチのもう1つの問題は、1つ以上の保存関数で形状を見落としたり、新しいプリミティブ形状が導入されても、保存ルーチンが1つのファイルタイプにしか実装されておらず、他のファイルタイプには実装されていないため、コードの拡張と保守の問題が発生する可能性があることです。同じファイルのバージョンが増えるにつれて、保守はより複雑になります。
Instead, the visitor pattern can be applied. It encodes the logical operation (i.e. save(image_tree)) on the whole hierarchy into one class (i.e. Saver) that implements the common methods for traversing the tree and describes virtual helper methods (i.e. save_circle, save_square, etc.) to be implemented for format specific behaviors. In the case of the CAD example, such format specific behaviors would be implemented by a subclass of Visitor (i.e. SaverPNG). As such, all duplication of type checks and traversal steps is removed. Additionally, the compiler now complains if a shape is omitted since it is now expected by the common base traversal/save function.
The visitor pattern may be used for iteration over container-like data structures just like Iterator pattern but with limited functionality.[3]:288 For example, iteration over a directory structure could be implemented by a function class instead of more conventional loop pattern. This would allow deriving various useful information from directories content by implementing a visitor functionality for every item while reusing the iteration code. It's widely employed in Smalltalk systems and can be found in C++ as well.[3]:289 A drawback of this approach, however, is that you can't break out of the loop easily or iterate concurrently (in parallel i.e. traversing two containers at the same time by a single i variable).[3]:289 The latter would require writing additional functionality for a visitor to support these features.[3]:289

In the UMLclass diagram above, the ElementA class doesn't implement a new operation directly. Instead, ElementA implements a dispatching operationaccept(visitor) that "dispatches" (delegates) a request to the "accepted visitor object" (visitor.visitElementA(this)). The Visitor1 class implements the operation (visitElementA(e:ElementA)). ElementB then implements accept(visitor) by dispatching to visitor.visitElementB(this). The Visitor1 class implements the operation (visitElementB(e:ElementB)).
The UMLsequence diagram shows the run-time interactions: The Client object traverses the elements of an object structure (ElementA,ElementB) and calls accept(visitor) on each element. First, the Client calls accept(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()).


The visitor pattern requires a programming language that supports single dispatch, as common object-oriented languages (such as C++, Java, Smalltalk, Objective-C, Swift, JavaScript, Python and C#) do. Under this condition, consider two objects, each of some class type; one is termed the element, and the other is visitor.
The visitor declares a visit method, which takes the element as an argument, for each class of element. Concrete visitors are derived from the visitor class and implement these visit methods, each of which implements part of the algorithm operating on the object structure. The state 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 argument. Concrete elements, derived from the element class, implement the accept method. In its simplest form, this is no more than a call to the visitor's visit method. Composite elements, which maintain a list of child objects, typically iterate over these, calling each child's accept method.
The client creates the object structure, directly or indirectly, and instantiates the concrete visitors. When an operation is to be performed which is implemented using the Visitor pattern, it calls the accept method of the top-level element(s).
When the accept method is called in the program, 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 type of the element, as known from within the implementation of the accept method, which is the same as the dynamic type of the element. (As a bonus, if the visitor can't handle an argument of the given element's type, then the compiler will catch the error.)
Thus, the implementation of the visit method is chosen based on both the dynamic type of the element and the dynamic type of the visitor. This effectively implements double dispatch. For languages whose object systems support multiple dispatch, not only single dispatch, such as Common Lisp or C# via the Dynamic Language Runtime (DLR), implementation of the visitor pattern is greatly simplified (a.k.a. Dynamic Visitor) by allowing use of simple function overloading to cover all the cases being visited. A dynamic visitor, provided it operates on public data only, conforms to the open/closed principle (since it does not modify extant structures) and to the single responsibility principle (since it implements the Visitor pattern in a separate component).
In this way, one algorithm can be written to traverse a graph of elements, and many different kinds of operations can be 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.
This example declares a separate ExpressionPrintingVisitor class that takes care of the printing. If the introduction of a new concrete visitor is desired, a new class will be created to implement the Visitor interface, and new implementations for the Visit methods will be provided. The existing classes (Literal and Addition) will remain unchanged.
namespaceWikipedia.Examples;usingSystem;interfaceIVisitor{voidVisit(Literalliteral);voidVisit(Additionaddition);}classExpressionPrintingVisitor:IVisitor{publicvoidVisit(Literalliteral){Console.WriteLine(literal.Value);}publicvoidVisit(Additionaddition){doubleleftValue=addition.Left.GetValue();doublerightValue=addition.Right.GetValue();doublesum=addition.GetValue();Console.WriteLine($"{leftValue} + {rightValue} = {sum}");}}abstractclassExpression{publicabstractvoidAccept(IVisitorvisitor);publicabstractdoubleGetValue();}classLiteral:Expression{publicLiteral(doublevalue){this.Value=value;}publicdoubleValue{get;set;}publicoverridevoidAccept(IVisitorvisitor){visitor.Visit(this);}publicoverridedoubleGetValue(){returnValue;}}classAddition:Expression{publicAddition(Expressionleft,Expressionright){Left=left;Right=right;}publicExpressionLeft{get;set;}publicExpressionRight{get;set;}public override void Accept ( IVisitor visitor ) { Left.Accept ( visitor ) ; Right.Accept ( visitor ) ; visitor.Visit ( this ) ; } public override double GetValue ( ) { return Left.GetValue ( ) + Right.GetValue ( ) ; } }public static class Program { public static void Main ( string [] args ) { // 1 + 2 + 3 の加算をエミュレートしますe = new ( new Addition ( new Literal ( 1 ), new Literal ( 2 ) ), new Literal ( 3 ) );ExpressionPrintingVisitor printingVisitor = new (); e . Accept ( printingVisitor ); Console . ReadKey (); } }この場合、ストリーム上に自身を出力する方法を知るのはオブジェクト自身の責任である。したがって、ここでのビジターはストリームではなく、オブジェクトである。
「クラスを作成するための構文はありません。クラスは、他のクラスにメッセージを送信することによって作成されます。」WriteStreamサブクラス: #ExpressionPrinter instanceVariableNames: '' classVariableNames: '' package: 'Wikipedia' .ExpressionPrinter >>write: anObject "オブジェクトにアクションを委譲します。オブジェクトは特別なクラスである必要はありません。メッセージ #putOn:" anObject putOn: self . ^ anObject を理解できるだけで十分です。オブジェクトサブクラス: #Expression instanceVariableNames: '' classVariableNames: '' package: 'Wikipedia' 。式のサブクラス: #Literal instanceVariableNames: 'value' classVariableNames: '' package: 'Wikipedia' 。Literalclass>>with:aValue"Class method for building an instance of the Literal class"^selfnewvalue:aValue;yourself.Literal>>value:aValue"Setter for value"value:=aValue.Literal>>putOn:aStream"A Literal object knows how to print itself"aStreamnextPutAll:valueasString.Expressionsubclass:#AdditioninstanceVariableNames:'left right'classVariableNames:''package:'Wikipedia'.Additionclass>>left:aright:b"Class method for building an instance of the Addition class"^selfnewleft:a;right:b;yourself.Addition>>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"aStreamnextPut:$(.leftputOn:aStream.aStreamnextPut:$+.rightputOn:aStream.aStreamnextPut:$).Objectsubclass:#PrograminstanceVariableNames:''classVariableNames:''package:'Wikipedia'.Program>>main|expressionstream|expression:=Additionleft: (Additionleft: (Literalwith:1) right: (Literalwith:2)) right: (Literalwith:3).stream:=ExpressionPrinteron: (Stringnew:100).streamwrite:expression.Transcriptshow:streamcontents.Transcriptflush.Go does not support method overloading, so the visit methods need different names. A typical visitor interface might be
typeVisitorinterface{visitWheel(wheelWheel)stringvisitEngine(engineEngine)stringvisitBody(bodyBody)stringvisitCar(carCar)string}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 car) 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 require slightly different actions to print properly, CarElementPrintVisitor dispatches actions based on the class of the argument passed to its visit method. CarElementDoVisitor, which is analogous to a save operation for a different file format, does likewise.

packageorg.wikipedia.examples;importjava.util.List;interfaceCarElement{voidaccept(CarElementVisitorvisitor);}interfaceCarElementVisitor{voidvisit(Bodybody);voidvisit(Carcar);voidvisit(Engineengine);voidvisit(Wheelwheel);}classWheelimplementsCarElement{privatefinalStringname;publicWheel(finalStringname){this.name=name;}publicStringgetName(){returnname;}@Overridepublicvoidaccept(CarElementVisitorvisitor){/* * accept(CarElementVisitor) in Wheel implements * accept(CarElementVisitor) in CarElement, so the call * to accept is bound at run time. This can be considered * the *first* dispatch. However, the decision to call * visit(Wheel) (as opposed to visit(Engine) etc.) can be * made during compile time since 'this' is known at compile * time to be a Wheel. Moreover, each implementation of * CarElementVisitor implements the visit(Wheel), which is * another decision that is made at run time. This can be * considered the *second* dispatch. */visitor.visit(this);}}classBodyimplementsCarElement{@Overridepublicvoidaccept(CarElementVisitorvisitor){visitor.visit(this);}}classEngineimplementsCarElement{@Overridepublicvoidaccept(CarElementVisitorvisitor){visitor.visit(this);}}classCarimplementsCarElement{privatefinalList<CarElement>elements;publicCar(){this.elements=List.of(newWheel("front left"),newWheel("front right"),newWheel("back left"),newWheel("back right"),newBody(),newEngine());}@Overridepublicvoidaccept(CarElementVisitorvisitor){for(CarElementelement:elements){element.accept(visitor);}visitor.visit(this);}}classCarElementDoVisitorimplementsCarElementVisitor{@Overridepublicvoidvisit(Bodybody){System.out.println("Moving my body");}@Overridepublicvoidvisit(Carcar){System.out.println("Starting my car");}@Overridepublicvoidvisit(Wheelwheel){System.out.printf("Kicking my %s wheel%n",wheel.getName());}@Overridepublicvoidvisit(Engineengine){System.out.println("Starting my engine");}}classCarElementPrintVisitorimplementsCarElementVisitor{@Overridepublicvoidvisit(Bodybody){System.out.println("Visiting body");}@Override public void visit ( Car car ) { System.out.println ( " Visiting car " ) ; }@Override public void visit ( Engine engine ) { System.out.println ( "エンジンを訪問しています" ) ; }@Override public void visit ( Wheel wheel ) { System . out . printf ( "Visiting %s wheel%n" , wheel . getName ()); } }public class VisitorDemo { public static void main ( String [] args ) { Car car = new Car ();car.accept ( new CarElementPrintVisitor ()) ; car.accept ( new CarElementDoVisitor ( ) ) ; } }左前輪を訪問 右前輪を訪問 左後輪を訪れる 右後輪を訪れる 訪問団体 訪問エンジン 訪問車 左前輪を蹴る 右前輪を蹴る 左後輪を蹴る 右後輪を蹴る 体を動かす エンジンを始動する 車を始動する
( defclass auto () (( elements :initarg :elements )))( defclass auto-part () (( name :initarg :name :initform "<unnamed-car-part>" )))( defmethod print-object (( p auto-part ) stream ) ( print-object ( slot-value p 'name ) stream ))( defclass wheel ( auto-part ) ())( defclass body ( auto-part ) ())( defclass engine ( auto-part ) ())( defgeneric traverse ( function object other-object ))(defmethodtraverse(function(aauto)other-object)(with-slots(elements)a(dolist(eelements)(funcallfunctioneother-object))));; do-something visitations;; catch all(defmethoddo-something(objectother-object)(formatt"don't know how ~s and ~s should interact~%"objectother-object));; visitation involving wheel and integer(defmethoddo-something((objectwheel)(other-objectinteger))(formatt"kicking wheel ~s ~s times~%"objectother-object));; visitation involving wheel and symbol(defmethoddo-something((objectwheel)(other-objectsymbol))(formatt"kicking wheel ~s symbolically using symbol ~s~%"objectother-object))(defmethoddo-something((objectengine)(other-objectinteger))(formatt"starting engine ~s ~s times~%"objectother-object))(defmethoddo-something((objectengine)(other-objectsymbol))(formatt"starting engine ~s symbolically using symbol ~s~%"objectother-object))( let (( a ( make-instance 'auto :elements ` ( , ( make-instance 'wheel :name "front-left-wheel" ) , ( make-instance 'wheel :name "front-right-wheel" ) , ( make-instance 'wheel :name "rear-left-wheel" ) , ( make-instance 'wheel :name "rear-right-wheel" ) , ( make-instance 'body :name "body" ) , ( make-instance 'engine :name "engine" ))))) ;; 要素を出力するためにトラバースします;; ここでは、ストリーム *standard-output* が other-object の役割を果たします( traverse #' *standard-output* を出力します)( terpri ) ;; 改行を出力;; 他のオブジェクトから任意のコンテキストでトラバース( traverse #' do-something a 42 );; 他のオブジェクトから任意のコンテキストでトラバースする( traverse #' do-something a 'abc ))「左前輪」 「右前輪」 「後輪左」 「右後輪」 "体" "エンジン" 左前輪を42回蹴る 右前輪を42回蹴る 後輪(左後輪)を42回蹴る 右後輪を42回蹴る 「body」と「42」がどのように相互作用すべきかはわかりません エンジン「エンジン」を42回起動 左前輪を蹴る様子を、記号ABCを用いて象徴的に表現する。 キックホイール「右前輪」を記号ABCを用いて象徴的に表現する 左後輪を蹴る様子を象徴的にABC記号で表す 後輪を蹴る「右後輪」を象徴的にABC記号で表す 「体」とABCがどのように相互作用すべきか分からない エンジンの始動を記号ABCを用いて象徴的に表す。
では、このother-objectパラメータは不要ですtraverse。理由は、字句的にキャプチャされたオブジェクトを使用して目的のターゲットメソッドを呼び出す匿名関数を使用できるためです。
(defmethodtraverse(function(aauto));; other-object removed(with-slots(elements)a(dolist(eelements)(funcallfunctione))));; from here too;; ...;; alternative way to print-traverse(traverse(lambda(o)(printo*standard-output*))a);; alternative way to do-something with;; elements of a and integer 42(traverse(lambda(o)(do-somethingo42))a)Now, the multiple dispatch occurs in the call issued from the body of the anonymous function, and so 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 function, in which there is no evidence of two objects being involved. All knowledge of there being two objects and a dispatch on their types is in the lambda function.
Python does not support method overloading in the classical sense (polymorphic behavior according to type of passed parameters), so the "visit" methods for the different model types need to have different names.
"""Visitor pattern example."""fromabcimportABCMeta,abstractmethodfromtypingimportNoReturnNOT_IMPLEMENTED:str="You should implement this."classCarElement(metaclass=ABCMeta):@abstractmethoddefaccept(self,visitor:CarElementVisitor)->NoReturn:raiseNotImplementedError(NOT_IMPLEMENTED)classBody(CarElement):defaccept(self,visitor:CarElementVisitor)->None:visitor.visit_body(self)classEngine(CarElement):defaccept(self,visitor:CarElementVisitor)->None:visitor.visit_engine(self)classWheel(CarElement):def__init__(self,name:str)->None:self.name=namedefaccept(self,visitor:CarElementVisitor)->None:visitor.visit_wheel(self)classCar(CarElement):def__init__(self)->None:self.elements:list[CarElement]=[Wheel("front left"),Wheel("front right"),Wheel("back left"),Wheel("back right"),Body(),Engine()]defaccept(self,visitor):forelementinself.elements:element.accept(visitor)visitor.visit_car(self)classCarElementVisitor(metaclass=ABCMeta):@abstractmethoddefvisit_body(self,element:CarElement)->NoReturn:raiseNotImplementedError(NOT_IMPLEMENTED)@abstractmethoddefvisit_engine(self,element:CarElement)->NoReturn:raiseNotImplementedError(NOT_IMPLEMENTED)@abstractmethoddefvisit_wheel(self,element:CarElement)->NoReturn:raiseNotImplementedError(NOT_IMPLEMENTED)@abstractmethoddefvisit_car(self,element:CarElement)->NoReturn:raiseNotImplementedError(NOT_IMPLEMENTED)classCarElementDoVisitor(CarElementVisitor):defvisit_body(self,body:Body)->None:print("Moving my body.")defvisit_car(self,car:Car)->None:print("Starting my car.")defvisit_wheel(self,wheel:Wheel)->None:print(f"Kicking my {wheel.name} wheel.")defvisit_engine(self,engine:Engine)->None:print("Starting my engine.")classCarElementPrintVisitor(CarElementVisitor):defvisit_body(self,body:Body)->None:print("Visiting body.")defvisit_car(self,car:Car)->None:print("Visiting car.")defvisit_wheel(self,wheel:Wheel)->None:print(f"Visiting {wheel.name} wheel.")defvisit_engine(self,engine:Engine)->None:print("Visiting engine.")if__name__=="__main__":car:Car=Car()car.accept(CarElementPrintVisitor())car.accept(CarElementDoVisitor())Visiting front left wheel.Visiting front right wheel.Visiting back left wheel.Visiting back right wheel.Visiting body.Visiting engine.Visiting car.Kicking my front left wheel.Kicking my front right wheel.Kicking my back left wheel.Kicking my back right wheel.Moving my body.Starting my engine.Starting my car.Using Python 3 or above allows to make a general implementation of the accept method:
classVisitable:defaccept(self,visitor:Visitor)->Any:lookup:str=f"visit_{self.__qualname__.replace(".","_")}"returngetattr(visitor,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 use the subclass hook feature to define the lookup in advance.
{{cite web}}: CS1 maint: url-status (link)