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