Repository navigation
Expand file tree
/
Copy pathvisitor.py
More file actions
119 lines (86 loc) · 5.06 KB
/
Copy pathvisitor.py
File metadata and controls
119 lines (86 loc) · 5.06 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
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List
class Component(ABC):
"""
Интерфейс Компонента объявляет метод accept, который в качестве аргумента
может получать любой объект, реализующий интерфейс посетителя.
"""
@abstractmethod
def accept(self, visitor: Visitor) -> None:
pass
class ConcreteComponentA(Component):
"""
Каждый Конкретный Компонент должен реализовать метод accept таким образом,
чтобы он вызывал метод посетителя, соответствующий классу компонента.
"""
def accept(self, visitor: Visitor) -> None:
"""
Обратите внимание, мы вызываем visitConcreteComponentA, что
соответствует названию текущего класса. Таким образом мы позволяем
посетителю узнать, с каким классом компонента он работает.
"""
visitor.visit_concrete_component_a(self)
def exclusive_method_of_concrete_component_a(self) -> str:
"""
Конкретные Компоненты могут иметь особые методы, не объявленные в их
базовом классе или интерфейсе. Посетитель всё же может использовать эти
методы, поскольку он знает о конкретном классе компонента.
"""
return "A"
class ConcreteComponentB(Component):
"""
То же самое здесь: 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):
"""
Интерфейс Посетителя объявляет набор методов посещения, соответствующих
классам компонентов. Сигнатура метода посещения позволяет посетителю
определить конкретный класс компонента, с которым он имеет дело.
"""
@abstractmethod
def visit_concrete_component_a(self, element: ConcreteComponentA) -> None:
pass
@abstractmethod
def visit_concrete_component_b(self, element: ConcreteComponentB) -> None:
pass
"""
Конкретные Посетители реализуют несколько версий одного и того же алгоритма,
которые могут работать со всеми классами конкретных компонентов.
Максимальную выгоду от паттерна Посетитель вы почувствуете, используя его со
сложной структурой объектов, такой как дерево Компоновщика. В этом случае было
бы полезно хранить некоторое промежуточное состояние алгоритма при выполнении
методов посетителя над различными объектами структуры.
"""
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:
"""
Клиентский код может выполнять операции посетителя над любым набором
элементов, не выясняя их конкретных классов. Операция принятия направляет
вызов к соответствующей операции в объекте посетителя.
"""
# ...
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)