In object oriented programming, the Visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures.

Visitor pattern is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Description

It is one way to follow the open/closed principle which states that: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".

The Visitor pattern is one of the twenty-three Gang of Four design patterns that facilitate the solution of recurring design problems in object-oriented software.

Operation

Consider two objects, each of some class type; one is termed the element, and the other is the 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. Composite elements, which maintain a list of child objects, typically iterate over these, calling each child's accept method.

Having created the object structure, a program should first instantiate the concrete visitors. When an operation is to be performed which is implemented using the Visitor pattern, it should then call the accept method of the top-level element(s) passing the visitor(s) as arguments.

Examples

The Wikipedia article contains examples of the Visitor pattern written in: C#, Smalltalk, Go (partial), Java, Common Lisp and Python.

Task

Demonstrate the workings of the Visitor pattern in your language by translating one (or more) of the Wikipedia examples. If your language is one of those for which an example already exists, try to translate one of the other examples.

If you don't know any of the example languages or prefer to use your own example, then this is also acceptable.

If your language does not support the object oriented paradigm at all (or only to a limited extent), then try to emulate the intent of the pattern with the tools it does have by writing a program which produces the same output as one of the Wikipedia examples.

References


Raku

Raku implements multiple dispatch so the visitor pattern is perhaps not as useful/necessary there. That said, it can be done fairly easily.

(Largely based on an example published by Johnathan Stowe.)

role CarElementVisitor { ... }

class CarElement {
    method accept(CarElementVisitor $visitor) {
        $visitor.visit: self
    }
}

class Body is CarElement { }

class Engine is CarElement { }

class Wheel is CarElement {
    has Str $.name is required;
}

class Car is CarElement {
    has CarElement @.elements = ( 
        Wheel.new(name => "front left"),
        Wheel.new(name => "front right"),
        Wheel.new(name => "rear left"),
        Wheel.new(name => "rear right"),
        Body.new,
        Engine.new
    );

    method accept(CarElementVisitor $visitor) {
        for @.elements -> $element { $element.accept: $visitor };
        $visitor.visit: self;
    }
}

role CarElementVisitor {
    method visit(CarElement $e) { ... }
}

class CarElementDoVisitor does CarElementVisitor {
    multi method visit(Body $e) {
        say "Moving my body.";
    }
    multi method visit(Car $e) {
        say "Starting my car.";
    }
    multi method visit(Wheel $e) {
        say "Kicking my { $e.name } wheel.";
    }
    multi method visit(Engine $e) {
        say "Starting my engine.";
    }
}

class CarElementPrintVisitor does CarElementVisitor {
    multi method visit(Body $e) {
        say "Visiting body.";
    }
    multi method visit(Car $e) {
        say "Visiting car.";
    }
    multi method visit(Wheel $e) {
        say "Visiting { $e.name } wheel.";
    }
    multi method visit(Engine $e) {
        say "Visiting engine.";
    }
}

my Car $car = Car.new;

$car.accept: CarElementPrintVisitor.new;
$car.accept: CarElementDoVisitor.new;
Output:
Visiting front left wheel.
Visiting front right wheel.
Visiting rear left wheel.
Visiting rear right wheel.
Visiting body.
Visiting engine.
Visiting car.
Kicking my front left wheel.
Kicking my front right wheel.
Kicking my rear left wheel.
Kicking my rear right wheel.
Moving my body.
Starting my engine.
Starting my car.

Wren

Translation of C# example

As is often the case in practice, the following example departs somewhat from the typical operation of the pattern described above. There is no abstract Visitor class - only a concrete Visitor class - and the 'visit' methods are called something else.

class ExpressionPrintingVisitor {
    construct new(){}

    printLiteral(literal) { System.print(literal.value) }

    printAddition(addition) {
        var leftValue   = addition.left.value
        var rightValue  = addition.right.value
        var sum = addition.value
        System.print("%(leftValue) + %(rightValue) = %(sum)")
    }
}

// abstract class
class Expression {
    accept(visitor) {}
    value {}
}

class Literal is Expression {
    construct new(value) {
        _value = value
    }

    value       { _value }
    value=(val) { _value = val }

    accept(visitor) {
        visitor.printLiteral(this)
    }
}

class Addition is Expression {
    construct new(left, right) {
        _left = left
        _right = right
    }

    left        { _left }
    left=(exp)  { _left = exp }

    right       { _right }
    right=(exp) { _right = exp }

    accept(visitor) {
        _left.accept(visitor)
        _right.accept(visitor)
        visitor.printAddition(this)
    }

    value { _left.value + _right.value }
}

// Emulate 1 + 2 + 3
var e = Addition.new(
    Addition.new(Literal.new(1), Literal.new(2)),
    Literal.new(3)
)
var printingVisitor = ExpressionPrintingVisitor.new()
e.accept(printingVisitor)
Output:
1
2
1 + 2 = 3
3
3 + 3 = 6

Translation of Java example

Library: Wren-str

Note that Wren is dynamically typed and can only overload methods based on arity and not on argument type. In the following example, rather than having separate methods for each element type, we instead have a single 'visit' method which tests the type of the argument at run time and takes the appropriate action.

import "./str" for Str

// abstract class
class CarElement {
    accept(visitor) {}
}

// abstract class
class CarElementVisitor {
    visit(obj) {}
}

class Wheel is CarElement {
    construct new(name) {
        _name = name
    }

    name { _name }

    accept(visitor) {
        visitor.visit(this)
    }
}

class Body is CarElement {
    construct new() {}

    accept(visitor) {
        visitor.visit(this)
    }
}

class Engine is CarElement {
    construct new() {}

    accept(visitor) {
        visitor.visit(this)
    }
}

class Car is CarElement {
    construct new() {
        _elements = [
            Wheel.new("front left"), Wheel.new("front right"),
            Wheel.new("back left"), Wheel.new("back right"),
            Body.new(), Engine.new()
        ]
    }

    accept(visitor) {
        for (element in _elements) element.accept(visitor)
        visitor.visit(this)
    }
}

class CarElementDoVisitor is CarElementVisitor {
    construct new() {}

    visit(obj) {
        if (obj is Body) {
            System.print("Moving my body")
        } else if (obj is Car) {
            System.print("Starting my car")
        } else if (obj is Wheel) {
            System.print("Kicking my %(obj.name) wheel")
        } else if (obj is Engine) {
            System.print("Starting my engine")
        }
    }
}

class CarElementPrintVisitor is CarElementVisitor {
    construct new() {}

    visit(obj) {
        if ((obj is Body) || (obj is Car) || (obj is Engine)) {
            System.print("Visiting %(Str.lower(obj.type))")
        } else if (obj is Wheel) {
            System.print("Visiting %(obj.name) wheel")
        }
    }
}

var car = Car.new()
car.accept(CarElementPrintVisitor.new())
car.accept(CarElementDoVisitor.new())
Output:
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