PHP Visitor Design Pattern II: The Double Dispatch

visitor2x

The Visitor pattern uses a double dispatch even with languages that are inherently single dispatch (such as PHP). In this second installment of the Visitor, I’d like to look at the concept and utility of double dispatch and role of the ObjectStructure in creating a working Visitor example that can be transformed into a practical application.

In Part I PHP Visitor Design Pattern I: The Single Dispatch, the focus was on using a single dispatch and a “pretend visitor.” This post shows how to create a visitor object based on both a Visitor interface and ConcreteVisitor implementations that are used in concert with the Element participants via the ObjectStructure. In order to do this, a couple more concrete Elements have been added to the example begun in Part I of the Visitor.

The Visitor Design Pattern Diagram

The Part I Visitor post suggests that the Visitor class diagram is a bit daunting, and so I held off until now to show it. If you look at the bottom portion of Figure 1, you will see that the example in Part I handled all of the Element participants, and included a “pretend visitor” where the Accept(Visitor) implementation in an actual Visitor pattern goes. So, you have some idea of what close to half of the Visitor does.

Figure 1: Visitor Class Diagram

Figure 1: Visitor Class Diagram

You can get a hint at what double-dispatch is by looking at the dual connections that the Client has to both the Visitor and Element (via the ObjectStructure). In the pattern, the Client is implied, but it’s clear to see the double reference to both the Visitor and the ObjectStructure which holds a reference to the Element.

This particular implementation of the Visitor pattern extends the example used in Part I where a “pretend” visitor is an operation that provides the fill color of SVG images. In this implementation, the fill color operation is provided by an actual visitor object. A third and forth Element class have been added, one with a visitor (Triangle) and one that does not have a visitor (zigzag lines have no fill colors—just a stroke color.) Figure 2 shows the program as a file diagram:

Figure 2: File diagram of Visitor pattern in PHP

Figure 2: File diagram of Visitor pattern in PHP

To get an idea of what the application does and look at the overall code, run the program and download the files:
PlayDownload

When you run the program, you can see that the shape (concrete Element) and the color (concrete Visitor) are selected separately. Those separate selections are to highlight the concept of double-dispatch. A shape and color are selected with the understanding that the developer had not included a fill color originally, and instead of starting from scratch to re-program the shape selector, the developer added a visitor. To see how double-dispatch works in a Visitor pattern, follow the path from the Client to the ObjectStructure and to the IElement::accept() method.

Double-Dispatch and Traversing Elements with Visitors

Figure 3: ObjectStructure and IElement::accept() double-dispatch link

Figure 3: ObjectStructure and IElement::accept() double-dispatch link

One of the key participants in the Visitor pattern is the ObjectStructure class. The Gang of Four introduce the ObjectStructure participant to handle traversing the Element objects that may need the attention of a Visitor. The pattern solves the double-dispatch problem by including a concrete visitor in the Element::accept(IVisitor) method. However, the path from Client-request to double-dispatch first goes through the ObjectStructure class. By looking the Client, ObjectStructure and a concrete Element, you can see the double-dispatch process:

<?php
/*
* CLIENT
*/
//Client.php
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", 1);
//Autoload code
function includeAll($className)
{
    include_once($className . '.php');
}
spl_autoload_register('includeAll');
 
//Begin Client class
class Client
{
    private static $shapeElement;
    private static $color;
    //client request
    public static function request()
    {
      self::$shapeElement=$_POST['shape'];
      self::$color=$_POST['color'];
 
      $obStructure = new ObjectStructure();
      $obStructure->attach(new self::$shapeElement());
      $colorVisitor= new self::$color();
      echo $obStructure->confirm($colorVisitor);
    } 
}
Client::request();
?>
 
 
<?php
/*
* OBJECT STRUCTURE
*/
//ObjectStructure.php
class ObjectStructure
{
    private $elements=array();
 
    public function attach(IElement $element)
    {
         array_push($this->elements,$element);
    }
 
    public function confirm(IVisitor $visitor)
    {
         foreach($this->elements as $elementNow)
         {
              return $elementNow->accept($visitor);
         }
    }
}
?>
 
 
<?php
/*
* ELEMENT
*/
//Circle.php
class Circle implements IElement
{
    private $visColor;
 
    public function accept(IVisitor $visitor)
    {
        $visitor->visitCircle($this);
        return $this->showShape();
    }
 
    private function showShape()
    {
        $circleShape= IElement::SVG . "<circle cx='110' cy='110' r='100' fill='{$this->doColor()}' stroke='blue' stroke-width='1' />";
        return $circleShape;
    }
 
    public function setColor($visitorColor)
    {
         $this->visColor = $visitorColor;
    }
 
    private function doColor()
    {
        return $this-> visColor;
    }
}
?>

Stepping through the process, the Client first (1) instantiates an instance of ObjectStructure. Second (2) the Client uses the ObjectStructure::attach() method to push the selected element instance onto an array. Third (3) the Client passes the selected visitor to the ObjectStructure::confirm() method, which in turn, fourth (4) calls the Element::accept($v) method which passes the concrete visitor to all of the elements in the array. Since this example is very simple, it only includes one element and one visitor at a time, and so the array will only contain a single element. However, because of the ObjectStructure class, you can add more elements if needed. (The following sections show more detail on how double-dispatch works and how the visitors are implemented.)

Figure 4 shows more detail of the concrete Element method that actually executes the double-dispatch:

Figure 4: The access() method is at the core of double-dispatch

Figure 4: The access() method is at the core of double-dispatch

The Client could have instantiated a new element class object (e.g., Circle) and a new visitor and called a ConcreteElement::accept() without going through the ObjectStructure.

Figure 5: Double-dispatch view directly from Client to Element and Visitor

Figure 5: Double-dispatch view directly from Client to Element and Visitor

Figure 5 provides a general outline of what the double-dispatch looks like without traversing the object structure. Figure 5 is not an alternative rendering of the Visitor pattern, but instead, by cutting out the traversal process of the ObjectStructure, it’s a bit easier to envision what is happening when double-dispatch is used. You can see both the point at which each is summoned by the Client and how they come together in the accept() method. If the Client request were changed from a call to the ObjectStructure to a concrete element such as the Circle class. The following snippets show the comparative differences:

$obStructure = new ObjectStructure();
$obStructure->attach(new self::$shapeElement());
$colorVisitor= new self::$color();
echo $obStructure->confirm($colorVisitor);

to

$circle = new self::$shapeElement(); //Element (ex: Circle)
$colorVisitor= new self::$color(); //Visitor
echo $circle->accept($colorVisitor);

the double call to both the IElement and IVisitor implementations is more pronounced when you omit the ObjectStructure. This does not mean that leaving out the object structure is a good idea.

Including the ObjectStructure in the Visitor Design Pattern is imperative!

Omitting the ObjectStructure results in the capacity of the Visitor to call only one implementation of an element with any request. As you have seen in this example, in cases where only a single element is called in a double-dispatch, the Visitor works perfectly fine with the ObjectStructure. However, it works perfectly well with multiple elements also. So, while you can see a bit more clearly how the double-dispatch operation works by removing the ObjectStructure; that does not mean you should leave it out of your pattern!

The Visitors

The visitor is simply an operation that does something that was not included in the original design of a program. Adding a visitor into a system that depends on complex objects is easy because it can be added without changing that complex structure. In the example the objects are not very complex for the purposes of clarity, but more complexity would not change the relationship between the visitors and the elements to which they add functionality.

I found the Visitor interface to be somewhat unique because it appears to violate a fundamental principles of Design Pattern OOP, namely,

Program to the interface and not the implementation.

Initially, in creating the IVisitor interface, I used type hinting to the IElement rather than any of the ConcreteElements, following the principle of programming to the interface. In doing so, everything worked fine. However, on closer examination of the details provided by The Gang of Four, it became clear that each visit operation should be to a specific concrete implementation, and so the final IVisitor interface is the following:

<?php
interface IVisitor
{
    function visitCircle(Circle $circle);
    function visitSquare(Square $square);
    function visitTriangle(Triangle $triangle);
}
?>

This interface means that each visitor operation must be spelled out for each concrete element. For example, in looking at the GreenVisitor, you can see that, with the exception of the Zigzag class that requires no fill color, all of the concrete elements are included:

<?php
class GreenVisitor implements IVisitor
{
    private $green="#0b0";
 
    function visitCircle(Circle $circle)
    {
       $circle->setColor($this->green);
    }
 
    function visitSquare(Square $square)
    {
        $square->setColor($this->green);
    }
 
    function visitTriangle(Triangle $triangle)
    {
        $triangle->setColor($this->green);
    }
}
?>

From looking at the GreenVisitor, you can see that the type hints are concrete elements. What this means is that if the visitor has different operations for each of the different concrete elements, the concrete visitors would be able to make further operation adjustments and fine tune the operation to the requirements of the individual concrete elements.

On the element side, the IElement does program the abstract method accept() to the visitor interface (IVisitor); not the concrete visitors:

<?php
interface IElement
{
    const SVG ="<svg width='2.2cm' height='2.2cm' viewBox='0 0 220 220'>";
 
    function accept(IVisitor $visitor);
}
?>

This means that the accept() method can receive any implementation of IVisitor; however, because the visitor methods call specific concrete elements, making a call with the wrong visitor would appear to be impossible. The original intention of the Visitor pattern is to add operations to an entire structure, but what if one of the concrete elements doesn’t need or use the additional operation. In this example, the Zigzag class generates a zigzag yellow line with no fill color—zigzags enclose no area for a fill color.

<?php
class Zigzag implements IElement
{
    private $visColor;
 
    public function accept(IVisitor $visitor)
    {
        return $this->showShape();
    }
    public function showShape()
    {
        $zigzag= IElement::SVG . "<polyline fill='none' stroke='#ff0' stroke-width='32' points='10,200 60,0 110,200 160,0 210,200'
   />";
        return $zigzag;
    }
}
?>

However, in order to implement the IElement interface, the Zigzag must include an implementation of accept(). In looking at this implementation of the Zigzag concrete element, you can see how the accept() method only requires that the visitor implements the IVisitor interface, and since the green, red or blue visitors all implement the same interface, as long as some visitor calls the accept() method, the Zigzag implementation of IElement works fine. It just does not use any of the fill colors provided by the visitor because there’s nothing to fill.

A better solution would be to have a concrete visitor with a “no fill” option and add a Zigzag concrete object to the IVisitor interface, but the solution here works as well—it just lacks a measure of elegance and completeness that obsessive-compulsive programmers such as myself like to have.

Share

Copyright © 2015 William Sanders. All Rights Reserved.

0 Responses to “PHP Visitor Design Pattern II: The Double Dispatch”


  • No Comments

Leave a Reply