Monday, October 28, 2013

Visitor Pattern

Visitor Pattern

Visitor pattern is used for adopting towards open-closed principle. Java supports single dispatch, i.e., overloads methods correctly when concrete implementation of one parameter is involved. But visitor pattern primarily relies on multiple dispatch (e.g., double dispatch if two parameters are involved) that dispatches a function call to different concrete functions depending on the runtime types of objects involved in the call. Double dispatch is ability to overload methods depending on the concrete types of the parameter. For example, if you want to achieve something as follows [1]. Please note here that the cartesian product of figures and printers need to established:

class Client {

 /** Prints all figures on each of the printers. */
 void printAllEverywhere( Figure[] figures, Printer[] printers ) {
 for ( int i = 0; i < figures.length; i++ ) {
 Figure figure = figures[ i ];
 for ( int j = 0; j < printers.length; j++ ) {
  Printer printer = printers[ j ];

  figure.printOn( printer ); 
  // must work for any printer or figure !
 }
 }
 }
 }
 
 interface Figure {
 void printOn( Printer printer );
 }
 interface Printer {
 void printCircle( Circle circle );
 void printRectangle( Rectangle rectangle );
 }
 
 
 class InkjetPrinter implements Printer {
 public void printCircle( Circle circle ) {
 // ... rasterizing logic for inkjet printing of circles here ...
 System.out.println( "Inkjet printer prints a cirlce." );
 }
 public void printRectangle( Rectangle rectangle ) {
 // ... rasterizing logic for inkjet printing of rectangles here ...
 System.out.println( "Inkjet printer prints a rectangle." );
 }
 }
 class PostscriptPrinter implements Printer {
 public void printCircle( Circle circle ) {
 // ... postscript preprocessing logic for circles here ...
 System.out.println( "PostScript printer prints a cirlce." );
 }
 public void printRectangle( Rectangle rectangle ) {
 // ... postscript preprocessing logic for rectangles here ...
 System.out.println( "PostScript printer prints a rectangle." );
 }
 }
 
 
  
 class Circle implements Figure {
 public void printOn( Printer printer ) {
 printer.printCircle( this ); // <-- the "trick" !
 }
 }
 class Rectangle implements Figure {
 public void printOn( Printer printer ) {
 printer.printRectangle( this );
 }
 }

In languages that support such as Nice, multimethods[1], this is much easy as it's possible to overload based on the type of the arguments.


 abstract class Figure {}
  abstract class Printer {}

  class Circle extends Figure {}
  class Rectangle extends Figure {}

  class InkjetPrinter extends Printer {}
  class PostscriptPrinter extends Printer {}

  void print(Printer printer, Figure shape);

  print(InkjetPrinter printer, Circle shape) {
 println("Inkjet printer prints a circle.");
  }

  print(PostscriptPrinter printer, Circle shape) {
 println("Postscript printer prints a circle.");
  }

  print(InkjetPrinter printer, Rectangle shape) {
 println("Inkjet printer prints a rectangle.");
  }

  print(PostscriptPrinter printer, Rectangle shape) {
 println("Postscript printer prints a rectangle.");
  }

  // Client code...
  void printAllEverywhere
 (Collection<Printer> printers,
 Collection<Figure> shapes)
  {
 printers.foreach(Printer p => shapes.foreach(Figure s => print(p,s)));
  }

  void main(String[] args)
  {
 printAllEverywhere
 ([ new PostscriptPrinter(), new InkjetPrinter() ],
 [ new Circle(), new Rectangle() ]);
  }


Open-closed principle dictates that behavior can be modified without altering source code. For example, change in source code implementation might have far reaching unintended changes such as changing test code, unit tests, code review, etc. Once an interface is finalized, the existing one is closed for modification, but a new one can be made to extend at least minimalistic behavior of the one that is closed.

This is hard to explain in a non-pictorial manner. But the essential players are:

1. Element is a class that has accept(Visitor v) method.
2. Visitor is a class that has visit(Element e) method.

Use visitor pattern for the following:

a. when many unrelated operations need to be performed on an object structure.
b. when the classes defining the object structure doesn't change often. You want to add/remove operations that are performed on it.
c. Object contains many sub-objects with varying interface.


Reference:

[1] http://c2.com/cgi/wiki?DoubleDispatchExample

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home