Clever Geek Handbook
📜 ⬆️ ⬇️

Visitor (design pattern)

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 ofbehavioral
Appointmentwithout changing the main class , add new operations to it.
StructureVisitorDiagram.svg
It is applied in caseswhen it is necessary for a number of classes to do a similar (one and the same) operation.
pros
  • new functionality is added to several classes at once, without changing the code of these classes;
  • allows you to get information about the type of object;
  • double scheduling;
  • the ability to describe your algorithm for each type of object .
Minuses
  • when changing the served class, you need to change the code for the template;
  • adding new classes is difficult because the hierarchy of the visitor and his sons needs to be updated.
Described in Design PatternsYes

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

  1. Add an accept(Visitor) method to the element hierarchy.
  2. Create a base Visitor class and define visit() methods for each item type.
  3. Create derived Visitor classes for each operation that is performed on the elements.
  4. The client creates a Visitor object and passes it to the accept(). method that is called accept().

C ++

C ++ implementation example
  #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

Java implementation example
  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 #

C # implementation example
  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 implementation example
  <? 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

Python implementation example
  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" (unspecified) . 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.
Source - https://ru.wikipedia.org/w/index.php?title=Visitor_(design_pattern)&oldid=100106113


More articles:

  • Shimonoseki
  • Starokachalovskaya Street (Metro Station)
  • Kamenyuka, Nikita Alexandrovich
  • Circulation Pump
  • Russell, John Scott
  • Volkswagen Eos
  • Shakya
  • Basketball at the 1963 Summer Universiade
  • Banzarov, Dorji
  • Pharaoh (play)

All articles

Clever Geek | 2019