Skip to content

Commit 84c5f4e

Browse files
committed
Visitor: add structural example
1 parent 41733a9 commit 84c5f4e

2 files changed

Lines changed: 171 additions & 0 deletions

File tree

src/Visitor/Structure/Output.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The client code works with all visitors via the base Visitor interface:
2+
A + ConcreteVisitor1
3+
B + ConcreteVisitor1
4+
It allows the same client code to work with different types of visitors:
5+
A + ConcreteVisitor2
6+
B + ConcreteVisitor2

src/Visitor/Structure/main.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"""
2+
EN: Visitor Design Pattern
3+
4+
Intent: Represent an operation to be performed over elements of an object
5+
structure. The Visitor pattern lets you define a new operation without
6+
changing the classes of the elements on which it operates.
7+
8+
RU: Паттерн Посетитель
9+
10+
Назначение: Позволяет добавлять в программу новые операции, не изменяя классы
11+
объектов, над которыми эти операции могут выполняться.
12+
"""
13+
14+
15+
from __future__ import annotations
16+
from abc import ABCMeta, abstractmethod
17+
from typing import List
18+
19+
20+
class Component(metaclass=ABCMeta):
21+
"""
22+
EN: The Component interface declares an `accept` method that should take the
23+
base visitor interface as an argument.
24+
25+
RU: Интерфейс Компонента объявляет метод accept, который в качестве
26+
аргумента может получать любой объект, реализующий интерфейс посетителя.
27+
"""
28+
29+
@abstractmethod
30+
def accept(self, visitor: Visitor) -> None:
31+
pass
32+
33+
34+
class ConcreteComponentA(Component):
35+
"""
36+
EN: Each Concrete Component must implement the `accept` method in such a way
37+
that it calls the visitor's method corresponding to the component's class.
38+
39+
RU: Каждый Конкретный Компонент должен реализовать метод accept таким
40+
образом, чтобы он вызывал метод посетителя, соответствующий классу компонента.
41+
"""
42+
43+
def accept(self, visitor: Visitor) -> None:
44+
"""
45+
EN: Note that we're calling `visitConcreteComponentA`, which matches the
46+
current class name. This way we let the visitor know the class of the
47+
component it works with.
48+
49+
RU: Обратите внимание, мы вызываем visitConcreteComponentA, что
50+
соответствует названию текущего класса. Таким образом мы позволяем
51+
посетителю узнать, с каким классом компонента он работает.
52+
"""
53+
54+
visitor.visit_concrete_component_a(self)
55+
56+
def exclusive_method_of_concrete_component_a(self) -> str:
57+
"""
58+
EN: Concrete Components may have special methods that don't exist in
59+
their base class or interface. The Visitor is still able to use these
60+
methods since it's aware of the component's concrete class.
61+
62+
RU: Конкретные Компоненты могут иметь особые методы, не объявленные в их
63+
базовом классе или интерфейсе. Посетитель всё же может использовать эти
64+
методы, поскольку он знает о конкретном классе компонента.
65+
"""
66+
67+
return "A"
68+
69+
70+
class ConcreteComponentB(Component):
71+
"""
72+
EN: Same here: visitConcreteComponentB => ConcreteComponentB
73+
74+
RU: То же самое здесь: visitConcreteComponentB => ConcreteComponentB
75+
"""
76+
77+
def accept(self, visitor: Visitor):
78+
visitor.visit_concrete_component_b(self)
79+
80+
def special_method_of_concrete_component_b(self) -> str:
81+
return "B"
82+
83+
84+
class Visitor(metaclass=ABCMeta):
85+
"""
86+
EN: The Visitor Interface declares a set of visiting methods that correspond
87+
to component classes. The signature of a visiting method allows the visitor
88+
to identify the exact class of the component that it's dealing with.
89+
90+
RU: Интерфейс Посетителя объявляет набор методов посещения, соответствующих
91+
классам компонентов. Сигнатура метода посещения позволяет посетителю
92+
определить конкретный класс компонента, с которым он имеет дело.
93+
"""
94+
95+
@abstractmethod
96+
def visit_concrete_component_a(self, element: ConcreteComponentA) -> None:
97+
pass
98+
99+
@abstractmethod
100+
def visit_concrete_component_b(self, element: ConcreteComponentB) -> None:
101+
pass
102+
103+
104+
"""
105+
EN: Concrete Visitors implement several versions of the same algorithm, which
106+
can work with all concrete component classes.
107+
108+
You can experience the biggest benefit of the Visitor pattern when using it
109+
with a complex object structure, such as a Composite tree. In this case, it
110+
might be helpful to store some intermediate state of the algorithm while
111+
executing visitor's methods over various objects of the structure.
112+
113+
RU: Конкретные Посетители реализуют несколько версий одного и того же
114+
алгоритма, которые могут работать со всеми классами конкретных компонентов.
115+
116+
Максимальную выгоду от паттерна Посетитель вы почувствуете, используя его со
117+
сложной структурой объектов, такой как дерево Компоновщика. В этом случае
118+
было бы полезно хранить некоторое промежуточное состояние алгоритма при
119+
выполнении методов посетителя над различными объектами структуры.
120+
"""
121+
122+
123+
class ConcreteVisitor1(Visitor):
124+
def visit_concrete_component_a(self, element) -> None:
125+
print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor1")
126+
127+
def visit_concrete_component_b(self, element) -> None:
128+
print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor1")
129+
130+
131+
class ConcreteVisitor2(Visitor):
132+
def visit_concrete_component_a(self, element) -> None:
133+
print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor2")
134+
135+
def visit_concrete_component_b(self, element) -> None:
136+
print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor2")
137+
138+
139+
def client_code(components: List[Component], visitor: Visitor) -> None:
140+
"""
141+
EN: The client code can run visitor operations over any set of elements
142+
without figuring out their concrete classes. The accept operation directs a
143+
call to the appropriate operation in the visitor object.
144+
145+
RU: Клиентский код может выполнять операции посетителя над любым набором
146+
элементов, не выясняя их конкретных классов. Операция принятия направляет
147+
вызов к соответствующей операции в объекте посетителя.
148+
"""
149+
150+
# ...
151+
for component in components:
152+
component.accept(visitor)
153+
# ...
154+
155+
156+
if __name__ == "__main__":
157+
components = [ConcreteComponentA(), ConcreteComponentB()]
158+
159+
print("The client code works with all visitors via the base Visitor interface:")
160+
visitor1 = ConcreteVisitor1()
161+
client_code(components, visitor1)
162+
163+
print("It allows the same client code to work with different types of visitors:")
164+
visitor2 = ConcreteVisitor2()
165+
client_code(components, visitor2)

0 commit comments

Comments
 (0)