Visitor pattern: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Wren}}: Changed to Wren S/H)
(Ada implementation with tagged types)
Line 35: Line 35:
<br>
<br>


=={{header|Ada}}==
An Ada implementation of the Wikipedia Java example.
===Method 1: via Tagged Types (the object-oriented approach)===
Perhaps more packages than needed (7), which makes for quite a few files (specification + implementation). Only the `Visitors` files are long; the rest are much shorter.
<syntaxhighlight lang="ada">pragma Ada_2022;


package Base is

type Base_Record is abstract tagged null record;

end Base;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

limited with Base;
limited with Bodies;
limited with Cars;
limited with Engines;
limited with Wheels;

package Visitors is

type Visitor is abstract tagged null record;

procedure Visit_Base
(Self : Visitor; Dest : Base.Base_Record'Class) is null;

procedure Visit_Body (Self : Visitor; Dest : Bodies.Body_Record'Class);
procedure Visit_Car (Self : Visitor; Dest : Cars.Car_Record'Class);
procedure Visit_Engine (Self : Visitor; Dest : Engines.Engine_Record'Class);
procedure Visit_Wheel (Self : Visitor; Dest : Wheels.Wheel_Record'Class);

type Perform is new Visitor with null record;
type Print is new Visitor with null record;

overriding procedure Visit_Body
(Self : Perform; Dest : Bodies.Body_Record'Class);
overriding procedure Visit_Body
(Self : Print; Dest : Bodies.Body_Record'Class);

overriding procedure Visit_Car
(Self : Perform; Dest : Cars.Car_Record'Class);
overriding procedure Visit_Car (Self : Print; Dest : Cars.Car_Record'Class);

overriding procedure Visit_Engine
(Self : Perform; Dest : Engines.Engine_Record'Class);
overriding procedure Visit_Engine
(Self : Print; Dest : Engines.Engine_Record'Class);

overriding procedure Visit_Wheel
(Self : Perform; Dest : Wheels.Wheel_Record'Class);
overriding procedure Visit_Wheel
(Self : Print; Dest : Wheels.Wheel_Record'Class);

end Visitors;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Ada.Text_IO;
with Ada.Strings.Unbounded;
use all type Ada.Strings.Unbounded.Unbounded_String;

with Wheels;

package body Visitors is

package IO renames Ada.Text_IO;

procedure Visit_Body (Self : Visitor; Dest : Bodies.Body_Record'Class) is
begin
Self.Visit_Body (Dest);
end Visit_Body;

overriding procedure Visit_Body
(Self : Perform; Dest : Bodies.Body_Record'Class)
is
begin
IO.Put_Line ("Moving my Body");
end Visit_Body;

overriding procedure Visit_Body
(Self : Print; Dest : Bodies.Body_Record'Class)
is
begin
IO.Put_Line ("Visiting Body");
end Visit_Body;

procedure Visit_Car (Self : Visitor; Dest : Cars.Car_Record'Class) is
begin
Self.Visit_Car (Dest);
end Visit_Car;

overriding procedure Visit_Car
(Self : Perform; Dest : Cars.Car_Record'Class)
is
begin
IO.Put_Line ("Starting my car");
end Visit_Car;

overriding procedure Visit_Car (Self : Print; Dest : Cars.Car_Record'Class)
is
begin
IO.Put_Line ("Visiting Car");
end Visit_Car;

procedure Visit_Engine (Self : Visitor; Dest : Engines.Engine_Record'Class)
is
begin
Self.Visit_Engine (Dest);
end Visit_Engine;

overriding procedure Visit_Engine
(Self : Perform; Dest : Engines.Engine_Record'Class)
is
begin
IO.Put_Line ("Revving my Engine");
end Visit_Engine;

overriding procedure Visit_Engine
(Self : Print; Dest : Engines.Engine_Record'Class)
is
begin
IO.Put_Line ("Visiting Engine");
end Visit_Engine;

procedure Visit_Wheel (Self : Visitor; Dest : Wheels.Wheel_Record'Class) is
begin
Self.Visit_Wheel (Dest);
end Visit_Wheel;

overriding procedure Visit_Wheel
(Self : Perform; Dest : Wheels.Wheel_Record'Class)
is
begin
IO.Put_Line ("Rolling my " & To_String (Dest.Name) & " wheel");
end Visit_Wheel;

overriding procedure Visit_Wheel
(Self : Print; Dest : Wheels.Wheel_Record'Class)
is
begin
IO.Put_Line ("Visiting " & To_String (Dest.Name) & " wheel");
end Visit_Wheel;

end Visitors;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Base;

limited with Visitors;

package Bodies is

type Body_Record is new Base.Base_Record with private;

procedure Visit (Self : Body_Record; Visitor : Visitors.Visitor'Class);

private

type Body_Record is new Base.Base_Record with null record;

end Bodies;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Visitors;

package body Bodies is

procedure Visit (Self : Body_Record; Visitor : Visitors.Visitor'Class) is
begin
Visitor.Visit_Body (Self);
end Visit;

end Bodies;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Base;
with Bodies;
with Engines;
with Wheels;

limited with Visitors;

package Cars is

type Car_Record is new Base.Base_Record with private;

procedure Visit (Self : Car_Record; Visitor : Visitors.Visitor'Class);

function Initialize return Car_Record;

private

type Wheel_Array is array (1 .. 4) of Wheels.Wheel_Record;

type Car_Record is new Base.Base_Record with record
Bod : Bodies.Body_Record;
Eng : Engines.Engine_Record;
Whs : Wheel_Array;
end record;

end Cars;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Ada.Strings.Unbounded;

with Visitors;

package body Cars is

subtype Unbounded_String is Ada.Strings.Unbounded.Unbounded_String;
use all type Unbounded_String;

procedure Visit (Self : Car_Record; Visitor : Visitors.Visitor'Class) is
begin
Visitor.Visit_Car (Self);
Visitor.Visit_Body (Self.Bod);
Visitor.Visit_Engine (Self.Eng);
for Wheel of Self.Whs loop
Visitor.Visit_Wheel (Wheel);
end loop;
end Visit;

function Initialize return Car_Record is
Result : Car_Record;
begin
Result.Whs :=
[Wheels.Initialize (To_Unbounded_String ("front left")),
Wheels.Initialize (To_Unbounded_String ("front right")),
Wheels.Initialize (To_Unbounded_String ("back left")),
Wheels.Initialize (To_Unbounded_String ("back right"))];
return Result;
end Initialize;

end Cars;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Base;

limited with Visitors;

package Engines is

type Engine_Record is new Base.Base_Record with private;

procedure Visit (Self : Engine_Record; Visitor : Visitors.Visitor'Class);

private

type Engine_Record is new Base.Base_Record with null record;

end Engines;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Visitors;

package body Engines is

procedure Visit (Self : Engine_Record; Visitor : Visitors.Visitor'Class) is
begin
Visitor.Visit_Engine (Self);
end Visit;

end Engines;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Ada.Strings.Unbounded;

with Base;

limited with Visitors;

package Wheels is

subtype Unbounded_String is Ada.Strings.Unbounded.Unbounded_String;
use all type Unbounded_String;

type Wheel_Record is new Base.Base_Record with private;

procedure Visit (Self : Wheel_Record; Visitor : Visitors.Visitor'Class);

function Initialize (Name : Unbounded_String) return Wheel_Record;

function Name (Me : Wheel_Record) return Unbounded_String;

private

type Wheel_Record is new Base.Base_Record with record
My_Name : Unbounded_String;
end record;

end Wheels;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Visitors;

package body Wheels is

procedure Visit (Self : Wheel_Record; Visitor : Visitors.Visitor'Class) is
begin
Visitor.Visit_Wheel (Self);
end Visit;

function Initialize (Name : Unbounded_String) return Wheel_Record is
(My_Name => Name);

function Name (Me : Wheel_Record) return Unbounded_String is (Me.My_Name);

end Wheels;
</syntaxhighlight>
<syntaxhighlight lang="ada">pragma Ada_2022;

with Cars;
with Visitors;

procedure Visitor_Pattern is
Car : Cars.Car_Record := Cars.Initialize;
Performer : aliased Visitors.Perform;
Printer : aliased Visitors.Print;
begin
Car.Visit (Performer);
Car.Visit (Printer);
end Visitor_Pattern;
</syntaxhighlight>
===Method 2: Discriminated Types===
=={{header|Julia}}==
=={{header|Julia}}==
<syntaxhighlight lang="julia">abstract type CarElementVisitor end
<syntaxhighlight lang="julia">abstract type CarElementVisitor end

Revision as of 03:25, 26 June 2024

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

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.

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


Ada

An Ada implementation of the Wikipedia Java example.

Method 1: via Tagged Types (the object-oriented approach)

Perhaps more packages than needed (7), which makes for quite a few files (specification + implementation). Only the `Visitors` files are long; the rest are much shorter.

pragma Ada_2022;

package Base is

   type Base_Record is abstract tagged null record;

end Base;
pragma Ada_2022;

limited with Base;
limited with Bodies;
limited with Cars;
limited with Engines;
limited with Wheels;

package Visitors is

   type Visitor is abstract tagged null record;

   procedure Visit_Base
     (Self : Visitor; Dest : Base.Base_Record'Class) is null;

   procedure Visit_Body (Self : Visitor; Dest : Bodies.Body_Record'Class);
   procedure Visit_Car (Self : Visitor; Dest : Cars.Car_Record'Class);
   procedure Visit_Engine (Self : Visitor; Dest : Engines.Engine_Record'Class);
   procedure Visit_Wheel (Self : Visitor; Dest : Wheels.Wheel_Record'Class);

   type Perform is new Visitor with null record;
   type Print is new Visitor with null record;

   overriding procedure Visit_Body
     (Self : Perform; Dest : Bodies.Body_Record'Class);
   overriding procedure Visit_Body
     (Self : Print; Dest : Bodies.Body_Record'Class);

   overriding procedure Visit_Car
     (Self : Perform; Dest : Cars.Car_Record'Class);
   overriding procedure Visit_Car (Self : Print; Dest : Cars.Car_Record'Class);

   overriding procedure Visit_Engine
     (Self : Perform; Dest : Engines.Engine_Record'Class);
   overriding procedure Visit_Engine
     (Self : Print; Dest : Engines.Engine_Record'Class);

   overriding procedure Visit_Wheel
     (Self : Perform; Dest : Wheels.Wheel_Record'Class);
   overriding procedure Visit_Wheel
     (Self : Print; Dest : Wheels.Wheel_Record'Class);

end Visitors;
pragma Ada_2022;

with Ada.Text_IO;
with Ada.Strings.Unbounded;
use all type Ada.Strings.Unbounded.Unbounded_String;

with Wheels;

package body Visitors is

   package IO renames Ada.Text_IO;

   procedure Visit_Body (Self : Visitor; Dest : Bodies.Body_Record'Class) is
   begin
      Self.Visit_Body (Dest);
   end Visit_Body;

   overriding procedure Visit_Body
     (Self : Perform; Dest : Bodies.Body_Record'Class)
   is
   begin
      IO.Put_Line ("Moving my Body");
   end Visit_Body;

   overriding procedure Visit_Body
     (Self : Print; Dest : Bodies.Body_Record'Class)
   is
   begin
      IO.Put_Line ("Visiting Body");
   end Visit_Body;

   procedure Visit_Car (Self : Visitor; Dest : Cars.Car_Record'Class) is
   begin
      Self.Visit_Car (Dest);
   end Visit_Car;

   overriding procedure Visit_Car
     (Self : Perform; Dest : Cars.Car_Record'Class)
   is
   begin
      IO.Put_Line ("Starting my car");
   end Visit_Car;

   overriding procedure Visit_Car (Self : Print; Dest : Cars.Car_Record'Class)
   is
   begin
      IO.Put_Line ("Visiting Car");
   end Visit_Car;

   procedure Visit_Engine (Self : Visitor; Dest : Engines.Engine_Record'Class)
   is
   begin
      Self.Visit_Engine (Dest);
   end Visit_Engine;

   overriding procedure Visit_Engine
     (Self : Perform; Dest : Engines.Engine_Record'Class)
   is
   begin
      IO.Put_Line ("Revving my Engine");
   end Visit_Engine;

   overriding procedure Visit_Engine
     (Self : Print; Dest : Engines.Engine_Record'Class)
   is
   begin
      IO.Put_Line ("Visiting Engine");
   end Visit_Engine;

   procedure Visit_Wheel (Self : Visitor; Dest : Wheels.Wheel_Record'Class) is
   begin
      Self.Visit_Wheel (Dest);
   end Visit_Wheel;

   overriding procedure Visit_Wheel
     (Self : Perform; Dest : Wheels.Wheel_Record'Class)
   is
   begin
      IO.Put_Line ("Rolling my " & To_String (Dest.Name) & " wheel");
   end Visit_Wheel;

   overriding procedure Visit_Wheel
     (Self : Print; Dest : Wheels.Wheel_Record'Class)
   is
   begin
      IO.Put_Line ("Visiting " & To_String (Dest.Name) & " wheel");
   end Visit_Wheel;

end Visitors;
pragma Ada_2022;

with Base;

limited with Visitors;

package Bodies is

   type Body_Record is new Base.Base_Record with private;

   procedure Visit (Self : Body_Record; Visitor : Visitors.Visitor'Class);

private

   type Body_Record is new Base.Base_Record with null record;

end Bodies;
pragma Ada_2022;

with Visitors;

package body Bodies is

   procedure Visit (Self : Body_Record; Visitor : Visitors.Visitor'Class) is
   begin
      Visitor.Visit_Body (Self);
   end Visit;

end Bodies;
pragma Ada_2022;

with Base;
with Bodies;
with Engines;
with Wheels;

limited with Visitors;

package Cars is

   type Car_Record is new Base.Base_Record with private;

   procedure Visit (Self : Car_Record; Visitor : Visitors.Visitor'Class);

   function Initialize return Car_Record;

private

   type Wheel_Array is array (1 .. 4) of Wheels.Wheel_Record;

   type Car_Record is new Base.Base_Record with record
      Bod : Bodies.Body_Record;
      Eng : Engines.Engine_Record;
      Whs : Wheel_Array;
   end record;

end Cars;
pragma Ada_2022;

with Ada.Strings.Unbounded;

with Visitors;

package body Cars is

   subtype Unbounded_String is Ada.Strings.Unbounded.Unbounded_String;
   use all type Unbounded_String;

   procedure Visit (Self : Car_Record; Visitor : Visitors.Visitor'Class) is
   begin
      Visitor.Visit_Car (Self);
      Visitor.Visit_Body (Self.Bod);
      Visitor.Visit_Engine (Self.Eng);
      for Wheel of Self.Whs loop
         Visitor.Visit_Wheel (Wheel);
      end loop;
   end Visit;

   function Initialize return Car_Record is
      Result : Car_Record;
   begin
      Result.Whs :=
        [Wheels.Initialize (To_Unbounded_String ("front left")),
        Wheels.Initialize (To_Unbounded_String ("front right")),
        Wheels.Initialize (To_Unbounded_String ("back left")),
        Wheels.Initialize (To_Unbounded_String ("back right"))];
      return Result;
   end Initialize;

end Cars;
pragma Ada_2022;

with Base;

limited with Visitors;

package Engines is

   type Engine_Record is new Base.Base_Record with private;

   procedure Visit (Self : Engine_Record; Visitor : Visitors.Visitor'Class);

private

   type Engine_Record is new Base.Base_Record with null record;

end Engines;
pragma Ada_2022;

with Visitors;

package body Engines is

   procedure Visit (Self : Engine_Record; Visitor : Visitors.Visitor'Class) is
   begin
      Visitor.Visit_Engine (Self);
   end Visit;

end Engines;
pragma Ada_2022;

with Ada.Strings.Unbounded;

with Base;

limited with Visitors;

package Wheels is

   subtype Unbounded_String is Ada.Strings.Unbounded.Unbounded_String;
   use all type Unbounded_String;

   type Wheel_Record is new Base.Base_Record with private;

   procedure Visit (Self : Wheel_Record; Visitor : Visitors.Visitor'Class);

   function Initialize (Name : Unbounded_String) return Wheel_Record;

   function Name (Me : Wheel_Record) return Unbounded_String;

private

   type Wheel_Record is new Base.Base_Record with record
      My_Name : Unbounded_String;
   end record;

end Wheels;
pragma Ada_2022;

with Visitors;

package body Wheels is

   procedure Visit (Self : Wheel_Record; Visitor : Visitors.Visitor'Class) is
   begin
      Visitor.Visit_Wheel (Self);
   end Visit;

   function Initialize (Name : Unbounded_String) return Wheel_Record is
     (My_Name => Name);

   function Name (Me : Wheel_Record) return Unbounded_String is (Me.My_Name);

end Wheels;
pragma Ada_2022;

with Cars;
with Visitors;

procedure Visitor_Pattern is
   Car       : Cars.Car_Record := Cars.Initialize;
   Performer : aliased Visitors.Perform;
   Printer   : aliased Visitors.Print;
begin
   Car.Visit (Performer);
   Car.Visit (Printer);
end Visitor_Pattern;

Method 2: Discriminated Types

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())
Output:

Same as Phix entry.

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.

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()
Output:
1.0
2.0
1.0 + 2.0 = 3.0
3.0
3.0 + 3.0 = 6.0

Phix

Quote of the day: Object oriented programs are offered as alternatives to correct ones... - Edsger Dijkstra
Completely beyond me why anyone would actually want(/need) this sort of nonsense, but there's nothing at all difficult here.

without javascript_semantics

abstract class CarElement
    public string name
    procedure Accept()
        throw("Derived classes *MUST* implement this")
    end procedure
end class

abstract class Visitable
    procedure Visit(CarElement e)
        throw("Derived classes *MUST* implement this")
    end procedure
end class

class CarPart extends CarElement
    procedure Accept(Visitable visitor)
        visitor.Visit(this)
    end procedure
end class

class Body extends CarPart
end class

class Engine extends CarPart
end class

class Wheel extends CarPart
    function Wheel(string name)
        this.name = name & " wheel"
        return this
    end function
end class

class Car extends CarPart
    sequence elements   
    function Car(string name)
        this.name = name
        elements = {new(Wheel,{"front left"}),
                    new(Wheel,{"front right"}),
                    new(Wheel,{"back left"}),
                    new(Wheel,{"back right"}),
                    new(Body,{"body"}),
                    new(Engine,{"engine"})}
        return this
    end function
    procedure Accept(Visitable visitor)
        CarElement element
        for element in elements do
            element.Accept(visitor)
        end for
        visitor.Visit(this)
    end procedure
end class

class CarElementPrintVisitor extends Visitable
    procedure Visit(CarElement e)
        printf(1,"Visiting %s.\n",{e.name})
    end procedure
end class

class CarElementDoVisitor extends Visitable
    procedure Visit(CarElement e)
        string verb
        if Body(e) then
            verb = "Moving"
        elsif Car(e) or Engine(e) then
            verb = "Starting"
        elsif Wheel(e) then
            verb = "Kicking"
        end if
        printf(1,"%s my %s.\n",{verb,e.name})
    end procedure
end class

Car car = new({"car"})
car.Accept(new(CarElementPrintVisitor))
car.Accept(new(CarElementDoVisitor))
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.

Python

This is based on the Wikipedia Python example, but uses structural pattern matching instead of multiple visit methods.

"""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())
Output:
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.

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