forked from Volodichev/patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcomposite.py
More file actions
160 lines (120 loc) · 6.26 KB
/
Copy pathcomposite.py
File metadata and controls
160 lines (120 loc) · 6.26 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
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List
class Component(ABC):
"""
Базовый класс Компонент объявляет общие операции как для простых, так и для
сложных объектов структуры.
"""
@property
def parent(self) -> Component:
return self._parent
@parent.setter
def parent(self, parent: Component):
"""
При необходимости базовый Компонент может объявить интерфейс для
установки и получения родителя компонента в древовидной структуре. Он
также может предоставить некоторую реализацию по умолчанию для этих
методов.
"""
self._parent = parent
"""
В некоторых случаях целесообразно определить операции управления потомками
прямо в базовом классе Компонент. Таким образом, вам не нужно будет
предоставлять конкретные классы компонентов клиентскому коду, даже во время
сборки дерева объектов. Недостаток такого подхода в том, что эти методы
будут пустыми для компонентов уровня листа.
"""
def add(self, component: Component) -> None:
pass
def remove(self, component: Component) -> None:
pass
def is_composite(self) -> bool:
"""
Вы можете предоставить метод, который позволит клиентскому коду понять,
может ли компонент иметь вложенные объекты.
"""
return False
@abstractmethod
def operation(self) -> str:
"""
Базовый Компонент может сам реализовать некоторое поведение по умолчанию
или поручить это конкретным классам, объявив метод, содержащий поведение
абстрактным.
"""
pass
class Leaf(Component):
"""
Класс Лист представляет собой конечные объекты структуры. Лист не может
иметь вложенных компонентов.
Обычно объекты Листьев выполняют фактическую работу, тогда как объекты
Контейнера лишь делегируют работу своим подкомпонентам.
"""
def operation(self) -> str:
return "Leaf"
class Composite(Component):
"""
Класс Контейнер содержит сложные компоненты, которые могут иметь вложенные
компоненты. Обычно объекты Контейнеры делегируют фактическую работу своим
детям, а затем «суммируют» результат.
"""
def __init__(self) -> None:
self._children: List[Component] = []
"""
Объект контейнера может как добавлять компоненты в свой список вложенных
компонентов, так и удалять их, как простые, так и сложные.
"""
def add(self, component: Component) -> None:
self._children.append(component)
component.parent = self
def remove(self, component: Component) -> None:
self._children.remove(component)
component.parent = None
def is_composite(self) -> bool:
return True
def operation(self) -> str:
"""
Контейнер выполняет свою основную логику особым образом. Он проходит
рекурсивно через всех своих детей, собирая и суммируя их результаты.
Поскольку потомки контейнера передают эти вызовы своим потомкам и так
далее, в результате обходится всё дерево объектов.
"""
results = []
for child in self._children:
results.append(child.operation())
return f"Branch({'+'.join(results)})"
def client_code(component: Component) -> None:
"""
Клиентский код работает со всеми компонентами через базовый интерфейс.
"""
print(f"RESULT: {component.operation()}", end="")
def client_code2(component1: Component, component2: Component) -> None:
"""
Благодаря тому, что операции управления потомками объявлены в базовом классе
Компонента, клиентский код может работать как с простыми, так и со сложными
компонентами, вне зависимости от их конкретных классов.
"""
if component1.is_composite():
component1.add(component2)
print(f"RESULT: {component1.operation()}", end="")
if __name__ == "__main__":
# Таким образом, клиентский код может поддерживать простые компоненты-
# листья...
simple = Leaf()
print("Client: I've got a simple component:")
client_code(simple)
print("\n")
# ...а также сложные контейнеры.
tree = Composite()
branch1 = Composite()
branch1.add(Leaf())
branch1.add(Leaf())
branch2 = Composite()
branch2.add(Leaf())
tree.add(branch1)
tree.add(branch2)
print("Client: Now I've got a composite tree:")
client_code(tree)
print("\n")
print("Client: I don't need to check the components classes even when managing the tree:")
client_code2(tree, simple)