Ce este
Șablonul Vizitator (Visitor) adaugă o operație nouă unei familii de tipuri fără a le modifica. Tipurile învață un singur lucru, o singură dată — accept(visitor) — iar de atunci încolo fiecare operație nouă este doar un vizitator nou, cu câte o metodă pentru fiecare tip.
El inversează compromisul obișnuit: adăugarea de operații devine ușoară, iar adăugarea de tipuri devine schimbarea costisitoare.
Exemplu: operații asupra formelor
Formele nu se mai schimbă niciodată. Afișarea ariilor este un vizitator, afișarea numelor este altul — iar un vizitator de export în SVG, adăugat mâine, nu ar atinge nici Circle, nici Rectangle.
Operații noi, tipuri neatinse
Ambii vizitatori parcurg aceeași listă de forme prin aceeași „ușă” accept. Fiecare vizitator păstrează întreaga operație într-un singur loc, în loc să o împrăștie prin tipuri.
Ieșire: program Python complet
from dataclasses import dataclass
from typing import Protocol
# Un vizitator este o operație întreagă asupra fiecărui tip de formă.
class Visitor(Protocol):
def visit_circle(self, circle: "Circle") -> None: ...
def visit_rectangle(self, rectangle: "Rectangle") -> None: ...
@dataclass
class Circle:
radius: float
def accept(self, visitor: Visitor) -> None:
visitor.visit_circle(self)
@dataclass
class Rectangle:
width: float
height: float
def accept(self, visitor: Visitor) -> None:
visitor.visit_rectangle(self)
class AreaPrinter:
def visit_circle(self, circle: Circle) -> None:
print(f"aria cercului: {3.14159 * circle.radius * circle.radius:.2f}")
def visit_rectangle(self, rectangle: Rectangle) -> None:
print(f"aria dreptunghiului: {rectangle.width * rectangle.height:.2f}")
class NamePrinter:
def visit_circle(self, circle: Circle) -> None:
print("un cerc")
def visit_rectangle(self, rectangle: Rectangle) -> None:
print("un dreptunghi")
shapes = [Circle(radius=2), Rectangle(width=3, height=4)]
for shape in shapes:
shape.accept(AreaPrinter())
for shape in shapes:
shape.accept(NamePrinter())
Când să-l folosești
Folosește-l când o familie stabilă de tipuri primește mereu operații noi — compilatoare care vizitează arbori sintactici, documente care primesc exportatori, forme care primesc funcții de desenare și de măsurare.
Evită-l când familia de tipuri încă crește: fiecare tip nou impune o modificare în fiecare vizitator. În multe programe Python, o instrucțiune match asupra tipurilor face aceeași treabă cu mai puțin mecanism — alege vizitatorul atunci când se înmulțesc operațiile, nu tipurile.
Avantaje și dezavantaje
Avantaje: fiecare operație întreagă stă într-o singură clasă; operațiile noi nu necesită nicio modificare a tipurilor vizitate; logica înrudită nu mai este împrăștiată.
Dezavantaje: dubla expediere (accept care apelează înapoi) îi încurcă pe începători; adăugarea unui singur tip vizitat înseamnă modificarea fiecărui vizitator; interfața familiei vizitate este înghețată devreme.