A visitor is a behavioral design pattern that describes an operation that is performed on objects of other classes. When changing the visitor, there is no need to change the classes served.
Visitor | |
---|---|
Visitor | |
Type of | behavioral |
Appointment | without changing the main class , add new operations to it. |
Structure | |
It is applied in cases | when it is necessary for a number of classes to do a similar (one and the same) operation. |
pros |
|
Minuses |
|
Described in Design Patterns | Yes |
The template demonstrates the classic technique of recovering information about lost types without resorting to downcasting of types using double dispatch.
Solved problem
It is necessary to do some incoherent operations on a number of objects, but it is necessary to avoid pollution of their code. And there is no possibility or desire to request the type of each node and cast the pointer to the correct type before performing the desired operation.
Task
One or more operations are performed on each object of some structure. You need to define a new operation without changing the classes of objects.
Solution
For independence, the visitor has a separate hierarchy. Structures have a certain interaction interface.
Usage
If there is a chance of changing the hierarchy of the served class, or it will be unstable or the open interface is efficient enough to access the template, then its use will be malicious.
A base Visitor
class is created with visit()
methods for each subclass of the parent Element
. Add an accept(visitor)
method to the Element hierarchy. For each operation that must be performed on Element
objects, create a class derived from Visitor
. Implementations of the visit()
method should use the public interface of the Element
class. The result: clients create Visitor
objects and pass them to each Element
object by calling accept()
.
Recommendations
The template should be used if:
- there are various objects of different classes with different interfaces, but you need to perform operations on them depending on specific classes;
- it is necessary to perform various operations that complicate the structure of the structure;
- new structure operations are often added.
Advantages and disadvantages
Advantages :
- adding new operations is simplified;
- Combining related operations in the
Visitor
class - the
Visitor
class may remember some kind of state as it traverses the container.
Disadvantages :
- adding new classes is difficult because the hierarchy of the visitor and his sons needs to be updated.
Implementation
- Add an
accept(Visitor)
method to the element hierarchy. - Create a base
Visitor
class and definevisit()
methods for each item type. - Create derived
Visitor
classes for each operation that is performed on the elements. - The client creates a
Visitor
object and passes it to theaccept().
method that is calledaccept().
C ++
#include <iostream>
#include <string>
class Foo ;
class Bar ;
class Baz ;
class Visitor {
public :
virtual void visit ( Foo & ref ) = 0 ;
virtual void visit ( Bar & ref ) = 0 ;
virtual void visit ( Baz & ref ) = 0 ;
virtual ~ Visitor () = default ;
};
class Element {
public :
virtual void accept ( Visitor & v ) = 0 ;
virtual ~ Element () = default ;
};
class Foo : public Element {
public :
void accept ( Visitor & v ) override {
v . visit ( * this );
}
};
class Bar : public Element {
public :
void accept ( Visitor & v ) override {
v . visit ( * this );
}
};
class Baz : public Element {
public :
void accept ( Visitor & v ) override {
v . visit ( * this );
}
};
class GetType : public Visitor {
public :
std :: string value ;
public :
void visit ( Foo & ref ) override {
value = "Foo" ;
}
void visit ( Bar & ref ) override {
value = "Bar" ;
}
void visit ( Baz & ref ) override {
value = "Baz" ;
}
};
int main () {
Foo foo ;
Bar bar ;
Baz baz ;
Element * elements [] = { & foo , & bar , & baz };
for ( auto elem : elements ) {
GetType visitor ;
elem -> accept ( visitor );
std :: cout << visitor . value << std :: endl ;
}
return 0 ;
}
Java
public class Demo {
public static void main ( String [] args ) {
Point p = new Point2d ( 1 , 2 );
Visitor v = new Chebyshev ();
p . accept ( v );
System out . println ( p . getMetric () );
}
}
interface Visitor {
public void visit ( Point2d p );
public void visit ( Point3d p );
}
abstract class Point {
public abstract void accept ( Visitor v );
private double metric = - 1 ;
public double getMetric () {
return metric ;
}
public void setMetric ( double metric ) {
this . metric = metric ;
}
}
class Point2d extends Point {
public Point2d ( double x , double y ) {
this . x = x ;
this . y = y ;
}
public void accept ( Visitor v ) {
v . visit ( this );
}
private double x ;
public double getX () { return x ; }
private double y ;
public double getY () { return y ; }
}
class Point3d extends Point {
public Point3d ( double x , double y , double z ) {
this . x = x ;
this . y = y ;
this . z is z ;
}
public void accept ( Visitor v ) {
v . visit ( this );
}
private double x ;
public double getX () { return x ; }
private double y ;
public double getY () { return y ; }
private double z ;
public double getZ () { return z ; }
}
class Euclid implements Visitor {
public void visit ( Point2d p ) {
p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () ) );
}
public void visit ( Point3d p ) {
p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () + p . getZ () * p . getZ () ) );
}
}
class Chebyshev implements Visitor {
public void visit ( Point2d p ) {
double ax = Math . abs ( p . getX () );
double ay = Math . abs ( p . getY () );
p . setMetric ( ax > ay ? ax : ay );
}
public void visit ( Point3d p ) {
double ax = Math . abs ( p . getX () );
double ay = Math . abs ( p . getY () );
double az = Math . abs ( p . getZ () );
double max = ax > ay ? ax : ay ;
if ( max < az ) max = az ;
p . setMetric ( max );
}
}
C #
public static class Demo
{
private static void Main ()
{
Point p = new Point2D ( 1 , 2 );
IVisitor v = new Chebyshev ();
p . Accept ( v );
Console WriteLine ( p . Metric );
}
}
internal interface IVisitor
{
void Visit ( Point2D p );
void Visit ( Point3D p );
}
internal abstract class Point
{
public double Metric { get ; set ; } = - 1 ;
public abstract void Accept ( IVisitor visitor );
}
internal class Point2D : Point
{
public Point2D ( double x , double y )
{
X = x ;
Y = y ;
}
public double X { get ; }
public double Y { get ; }
public override void Accept ( IVisitor visitor )
{
visitor . Visit ( this );
}
}
internal class Point3D : Point
{
public Point3D ( double x , double y , double z )
{
X = x ;
Y = y ;
Z is z ;
}
public double X { get ; }
public double Y { get ; }
public double Z { get ; }
public override void Accept ( IVisitor visitor )
{
visitor . Visit ( this );
}
}
internal class Euclid : IVisitor
{
public void Visit ( Point2D p )
{
p . Metric = Math . Sqrt ( p . X * p . X + p . Y * p . Y );
}
public void Visit ( Point3D p )
{
p . Metric = Math . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z );
}
}
internal class Chebyshev : IVisitor
{
public void Visit ( Point2D p )
{
var ax = Math . Abs ( p . X );
var ay = Math . Abs ( p . Y );
p . Metric = ax > ay ? ax : ay ;
}
public void Visit ( Point3D p )
{
var ax = Math . Abs ( p . X );
var ay = Math . Abs ( p . Y );
var az = Math . Abs ( p . Z );
var max = ax > ay ? ax : ay ;
if ( max < az ) max = az ;
p . Metric = max ;
}
}
PHP
<? php
interface Visitor {
public function visit ( Point $ point );
}
abstract class Point {
public abstract function accept ( Visitor $ visitor );
private $ _metric = - 1 ;
public function getMetric () {
return $ this -> _metric ;
}
public function setMetric ( $ metric ) {
$ this -> _metric = $ metric ;
}
}
class Point2d extends Point {
public function __construct ( $ x , $ y ) {
$ this -> _x = $ x ;
$ this -> _y = $ y ;
}
public function accept ( Visitor $ visitor ) {
$ visitor -> visit ( $ this );
}
private $ _x ;
public function getX () { return $ this -> _x ; }
private $ _y ;
public function getY () { return $ this -> _y ; }
}
class Point3d extends Point {
public function __construct ( $ x , $ y , $ z ) {
$ this -> _x = $ x ;
$ this -> _y = $ y ;
$ this -> _z = $ z ;
}
public function accept ( Visitor $ visitor ) {
$ visitor -> visit ( $ this );
}
private $ _x ;
public function getX () { return $ this -> _x ; }
private $ _y ;
public function getY () { return $ this -> _y ; }
private $ _z ;
public function getZ () { return $ this -> _z ; }
}
class Euclid implements Visitor {
public function visit ( Point $ p ) {
if ( $ p instanceof Point2d )
$ p -> setMetric ( sqrt ( $ p -> getX () * $ p -> getX () + $ p -> getY () * $ p -> getY () ) );
elseif ( $ p instanceof Point3d )
$ p -> setMetric ( sqrt ( $ p -> getX () * $ p -> getX () + $ p -> getY () * $ p -> getY () + $ p -> getZ () * $ p -> getZ () ) );
}
}
class Chebyshev implements Visitor {
public function visit ( Point $ p ) {
if ( $ p instanceof Point2d ) {
$ ax = abs ( $ p -> getX () );
$ ay = abs ( $ p -> getY () );
$ p -> setMetric ( $ ax > $ ay ? $ ax : $ ay );
}
elseif ( $ p instanceof Point3d ) {
$ ax = abs ( $ p -> getX () );
$ ay = abs ( $ p -> getY () );
$ az = abs ( $ p -> getZ () );
$ max = $ ax > $ ay ? $ ax : $ ay ;
if ( $ max < $ az ) $ max = $ az ;
$ p -> setMetric ( $ max );
}
}
}
function start () {
$ p = new Point2d ( 1 , 2 );
$ v = new Chebyshev ();
$ p -> accept ( $ v );
echo ( $ p -> getMetric () );
};
start ();
Python
from abc import ABCMeta , abstractmethod
class Spy ( metaclass = ABCMeta ):
"" "
Spy Visitor
"" "
@abstractmethod
def visit ( self , facility ) -> None :
"" "
Visit a military facility
"" "
pass
class MilitaryFacility ( metaclass = ABCMeta ):
"" "
Military Facility — Visited Facility
"" "
@abstractmethod
def accept ( self , spy : Spy ) -> None :
"" "
Accept Visitor Spy
"" "
pass
class MilitaryBase ( MilitaryFacility ):
"" "
Submarine Fleet Military Base
"" "
def __init__ ( self ) -> None :
self . _secret_draftings = 1
self . _nuclear_submarines = 1
def __repr__ ( self ) -> str :
return 'At the military base are {} nuclear submarines and {} secret drawings' . format (
self . _nuclear_submarines , self . _secret_draftings
)
def accept ( self , spy : Spy ) -> None :
spy . visit ( self )
def remove_secret_draftings ( self ) -> None :
if self . _secret_draftings :
self . _secret_draftings - = 1
def remove_nuclear_submarine ( self ) -> None :
if self . _nuclear_submarines :
self . _nuclear_submarines - = 1
class Headquarters ( MilitaryFacility ):
"" "
Army Headquarters
"" "
def __init__ ( self ) -> None :
self . _generals = 3
self . _secret_documents = 2
def __repr__ ( self ) -> str :
return 'At headquarters there are {} generals and {} secret documents' . format (
self . _generals , self . _secret_documents
)
def accept ( self , spy : Spy ) -> None :
spy . visit ( self )
def remove_general ( self ) -> None :
if self . _generals :
self . _generals - = 1
def remove_secret_documents ( self ) -> None :
if self . _secret_documents :
self . _secret_documents - = 1
class JamesBond ( Spy ):
"" "
Concrete spy
"" "
def visit ( self , facility : MilitaryFacility ) -> None :
if isinstance ( facility , MilitaryBase ): # James Bond visits a military base
facility . remove_secret_draftings () # steals secret drawings
facility . remove_nuclear_submarine () # and finally detonates a nuclear submarine
elif isinstance ( facility , Headquarters ): # James Bond visits headquarters
facility . remove_general () # ...
facility . remove_general () # ...
facility . remove_secret_documents () # ...
facility . remove_general () # sequentially destroys all generals
facility . remove_secret_documents () # and steals all secret documents
if __name__ == '__main__' :
base = MilitaryBase ()
hq = Headquarters ()
spy = JamesBond ()
base . accept ( spy )
hq . accept ( spy )
print ( 'OUTPUT:' ) # James Bond visits were truly disruptive
print ( base )
print ( hq )
'' '
OUTPUT:
There are 0 nuclear submarines and 0 secret blueprints at the military base
At headquarters there are 0 generals and 0 secret documents
'' '
Literature
- E. Gamma, R. Helm, R. Johnson, J. Vlissides . Receptions of object-oriented design. Design patterns. - St. Petersburg: Peter, 2001 .-- 368 p. - ISBN 5-272-00355-1 .
Links
- Robert C. Martin, Prentice Hall. The visitor family of design patterns by Robert C. Martin - a rough chapter from "The principles, patterns, and practices of agile software development" . Date of treatment September 1, 2010. Archived April 4, 2012.
- Design template visitor (visitor) . Purpose, description, features and implementation in C ++.
- Visitor Description, purpose, examples of implementation in java.