Visitor pattern: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
(Created a new draft task and added a Wren solution.)
 
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(9 intermediate revisions by 5 users not shown)
Line 34:
* [https://www.codeproject.com/Articles/588882/TheplusVisitorplusPatternplusExplained Code Project - The Visitor Pattern Explained]
<br>
 
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">abstract type CarElementVisitor end
 
struct CarElementDoVisitor <: CarElementVisitor end
struct CarElementPrintVisitor <: CarElementVisitor end
 
abstract type CarElement end
struct Body <: CarElement end
struct Engine <: CarElement end
 
struct Wheel <: CarElement
name::String
Wheel(str::String) = new(str)
end
 
struct Car <:CarElement
elements::Vector{CarElement}
Car() = new([Wheel("front left"), Wheel("front right"),
Wheel("rear left"), Wheel("rear right"),
Body(), Engine()])
end
 
accept(e::CarElement, visitor::CarElementVisitor) = visit(visitor, e)
 
function accept(car::Car, visitor::CarElementVisitor)
for element in car.elements
accept(element, visitor)
end
visit(visitor, car)
end
 
visit(v::CarElementDoVisitor, e::Body) = println("Moving my body.")
visit(v::CarElementDoVisitor, e::Car) = println("Starting my car.")
visit(v::CarElementDoVisitor, e::Wheel) = println("Kicking my $(e.name) wheel.")
visit(v::CarElementDoVisitor, e::Engine) = println("Starting my engine.")
 
visit(v::CarElementPrintVisitor, e::Body) = println("Visiting body.")
visit(v::CarElementPrintVisitor, e::Car) = println("Visiting car.")
visit(v::CarElementPrintVisitor, e::Wheel) = println("Visiting $(e.name) wheel.")
visit(v::CarElementPrintVisitor, e::Engine) = println("Visiting engine.")
 
car = Car()
accept(car, CarElementPrintVisitor())
println()
accept(car, CarElementDoVisitor())
</syntaxhighlight>{{out}} Same as Phix entry.
 
=={{header|Nim}}==
This is a translation of the Wikipedia C# example.
 
Note that Nim has no notion of “class” but only object types which allow simple inheritance. But it provides a way to define methods with dynamic dispatch and allows procedure overloading. So the translation of the C# example is easy.
<syntaxhighlight lang="Nim">import std/strutils
 
type
 
ExpressionPrintingVisitor = object
 
Expression = ref object of RootObj
 
Literal = ref object of Expression
value: float
 
Addition = ref object of Expression
left, right: Expression
 
 
# Expression procedures and methods.
 
method accept(e: Expression; v: ExpressionPrintingVisitor) {.base.} =
raise newException(CatchableError, "Method without implementation override")
 
method getValue(e: Expression): float {.base.} =
raise newException(CatchableError, "Method without implementation override")
 
 
# ExpressionPrintingVisitor procedures.
 
proc printLiteral(v: ExpressionPrintingVisitor; literal: Literal) =
echo literal.value
 
proc printAddition(v: ExpressionPrintingVisitor; addition: Addition) =
let leftValue = addition.left.getValue()
let rightValue = addition.right.getValue()
let sum = addition.getValue()
echo "$1 + $2 = $3".format(leftValue, rightValue, sum)
 
 
# Literal procedure and methods.
proc newLiteral(value: float): Literal =
Literal(value: value)
 
method accept(lit: Literal; v: ExpressionPrintingVisitor) =
v.printLiteral(lit)
 
method getValue(lit: Literal): float = lit.value
 
 
# Addition procedure and methods.
proc newAddition(left, right: Expression): Addition =
Addition(left: left, right: right)
 
method accept(a: Addition; v: ExpressionPrintingVisitor) =
a.left.accept(v)
a.right.accept(v)
v.printAddition(a)
 
method getValue(a: Addition): float =
a.left.getValue() + a.right.getValue()
 
 
proc main() =
# Emulate 1 + 2 + 3.
let e = newAddition(
newAddition(newLiteral(1), newLiteral(2)),
newLiteral(3))
var printingVisitor: ExpressionPrintingVisitor
e.accept(printingVisitor)
 
main()
</syntaxhighlight>
 
{{out}}
<pre>1.0
2.0
1.0 + 2.0 = 3.0
3.0
3.0 + 3.0 = 6.0</pre>
 
=={{header|Phix}}==
Quote of the day: Object oriented programs are offered as alternatives to correct ones... - Edsger Dijkstra<br>
Completely beyond me why anyone would actually want(/need) this sort of nonsense, but there's nothing at all difficult here.
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #008080;">without</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">abstract</span> <span style="color: #008080;">class</span> <span style="color: #000000;">CarElement</span>
<span style="color: #008080;">public</span> <span style="color: #004080;">string</span> <span style="color: #000000;">name</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Accept</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Derived classes *MUST* implement this"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">abstract</span> <span style="color: #008080;">class</span> <span style="color: #000000;">Visitable</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Visit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">CarElement</span> <span style="color: #000000;">e</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Derived classes *MUST* implement this"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">CarPart</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">CarElement</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Accept</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Visitable</span> <span style="color: #000000;">visitor</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">visitor</span><span style="color: #0000FF;">.</span><span style="color: #000000;">Visit</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">this</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">Body</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">CarPart</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">Engine</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">CarPart</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">Wheel</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">CarPart</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">Wheel</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">name</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">this</span><span style="color: #0000FF;">.</span><span style="color: #000000;">name</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">name</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" wheel"</span>
<span style="color: #008080;">return</span> <span style="color: #7060A8;">this</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">Car</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">CarPart</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">elements</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">Car</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">name</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">this</span><span style="color: #0000FF;">.</span><span style="color: #000000;">name</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">name</span>
<span style="color: #000000;">elements</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Wheel</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"front left"</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Wheel</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"front right"</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Wheel</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"back left"</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Wheel</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"back right"</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Body</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"body"</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Engine</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">"engine"</span><span style="color: #0000FF;">})}</span>
<span style="color: #008080;">return</span> <span style="color: #7060A8;">this</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Accept</span><span style="color: #0000FF;">(</span><span style="color: #000000;">Visitable</span> <span style="color: #000000;">visitor</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">CarElement</span> <span style="color: #000000;">element</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">element</span> <span style="color: #008080;">in</span> <span style="color: #000000;">elements</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">element</span><span style="color: #0000FF;">.</span><span style="color: #000000;">Accept</span><span style="color: #0000FF;">(</span><span style="color: #000000;">visitor</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">visitor</span><span style="color: #0000FF;">.</span><span style="color: #000000;">Visit</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">this</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">CarElementPrintVisitor</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">Visitable</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Visit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">CarElement</span> <span style="color: #000000;">e</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Visiting %s.\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">e</span><span style="color: #0000FF;">.</span><span style="color: #000000;">name</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #008080;">class</span> <span style="color: #000000;">CarElementDoVisitor</span> <span style="color: #008080;">extends</span> <span style="color: #000000;">Visitable</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">Visit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">CarElement</span> <span style="color: #000000;">e</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">verb</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">Body</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">verb</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Moving"</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">Car</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">or</span> <span style="color: #000000;">Engine</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">verb</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Starting"</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">Wheel</span><span style="color: #0000FF;">(</span><span style="color: #000000;">e</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">verb</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"Kicking"</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s my %s.\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">verb</span><span style="color: #0000FF;">,</span><span style="color: #000000;">e</span><span style="color: #0000FF;">.</span><span style="color: #000000;">name</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">class</span>
<span style="color: #000000;">Car</span> <span style="color: #000000;">car</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new</span><span style="color: #0000FF;">({</span><span style="color: #008000;">"car"</span><span style="color: #0000FF;">})</span>
<span style="color: #000000;">car</span><span style="color: #0000FF;">.</span><span style="color: #000000;">Accept</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">CarElementPrintVisitor</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">car</span><span style="color: #0000FF;">.</span><span style="color: #000000;">Accept</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">new</span><span style="color: #0000FF;">(</span><span style="color: #000000;">CarElementDoVisitor</span><span style="color: #0000FF;">))</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
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.
</pre>
 
=={{header|Python}}==
 
This is based on the [[wp:Visitor_pattern|Wikipedia]] Python example, but uses structural pattern matching instead of multiple visit methods.
 
<syntaxhighlight lang="python">
"""An example of the visitor pattern using structural pattern matching.
 
Requires Python >= 3.10.
"""
from __future__ import annotations
 
from abc import ABC
from abc import abstractmethod
 
 
class CarElement(ABC):
def accept(self, visitor: CarElementVisitor) -> None:
visitor.visit(self)
 
 
class CarElementVisitor(ABC):
@abstractmethod
def visit(self, car_element: CarElement) -> None:
"""Override this in `CarElementVisitor` subclasses."""
 
 
class Body(CarElement):
"""Car body."""
 
 
class Engine(CarElement):
"""Car engine."""
 
 
class Wheel(CarElement):
"""Car wheel"""
 
def __init__(self, name: str) -> None:
self.name = name
 
 
class Car(CarElement):
def __init__(self) -> None:
self.elements: list[CarElement] = [
Wheel("front left"),
Wheel("front right"),
Wheel("back left"),
Wheel("back right"),
Body(),
Engine(),
]
 
def accept(self, visitor: CarElementVisitor) -> None:
for element in self.elements:
visitor.visit(element)
super().accept(visitor)
 
 
class CarElementDoVisitor(CarElementVisitor):
def visit(self, car_element: CarElement) -> None:
match car_element:
case Body():
print("Moving my body.")
case Car():
print("Starting my car.")
case Wheel() as wheel:
print(f"Kicking my {wheel.name} wheel.")
case Engine():
print("Starting my engine.")
 
 
class CarElementPrintVisitor(CarElementVisitor):
def visit(self, car_element: CarElement) -> None:
match car_element:
case Body():
print("Visiting body.")
case Car():
print("Visiting car.")
case Wheel() as wheel:
print(f"Visiting my {wheel.name} wheel.")
case Engine():
print("Visiting my engine.")
 
 
if __name__ == "__main__":
car = Car()
car.accept(CarElementPrintVisitor())
car.accept(CarElementDoVisitor())
</syntaxhighlight>
 
{{out}}
<pre>
Visiting my front left wheel.
Visiting my front right wheel.
Visiting my back left wheel.
Visiting my back right wheel.
Visiting body.
Visiting my 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.
</pre>
 
=={{header|Raku}}==
Raku implements [[wp:Multiple_dispatch#Raku|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 [https://github.com/jonathanstowe/raku-patterns/blob/master/Behavioural/Visitor/visitor-simple example published by Johnathan Stowe].)
<syntaxhighlight lang="Raku">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;</syntaxhighlight>
 
{{out}}
<pre>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.</pre>
 
=={{header|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.
<syntaxhighlight lang="ecmascriptwren">class ExpressionPrintingVisitor {
construct new(){}
 
Line 111 ⟶ 540:
{{libheader|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.
<syntaxhighlight lang="ecmascriptwren">import "./str" for Str
 
// abstract class
9,476

edits