Skip to content

Commit 8815f78

Browse files
committed
implementing examples for design patterns
1 parent 88f18bd commit 8815f78

File tree

10 files changed

+847
-0
lines changed

10 files changed

+847
-0
lines changed

adapter/adapter_via_inheritance.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
class Target:
2+
"""
3+
The Target defines the domain-specific interface used by the client code.
4+
"""
5+
6+
def request(self) -> str:
7+
return "Target: The default target's behavior."
8+
9+
10+
class Adaptee:
11+
"""
12+
The Adaptee contains some useful behavior, but its interface is incompatible
13+
with the existing client code. The Adaptee needs some adaptation before the
14+
client code can use it.
15+
"""
16+
17+
def specific_request(self) -> str:
18+
return ".eetpadA eht fo roivaheb laicepS"
19+
20+
21+
class Adapter(Target, Adaptee):
22+
"""
23+
The Adapter makes the Adaptee's interface compatible with the Target's
24+
interface via multiple inheritance.
25+
"""
26+
27+
def request(self) -> str:
28+
return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}"
29+
30+
31+
def client_code(target: "Target") -> None:
32+
"""
33+
The client code supports all classes that follow the Target interface.
34+
"""
35+
36+
print(target.request(), end="")
37+
38+
39+
if __name__ == "__main__":
40+
print("Client: I can work just fine with the Target objects:")
41+
target = Target()
42+
client_code(target)
43+
print("\n")
44+
45+
adaptee = Adaptee()
46+
print(
47+
"Client: The Adaptee class has a weird interface. "
48+
"See, I don't understand it:"
49+
)
50+
print(f"Adaptee: {adaptee.specific_request()}", end="\n\n")
51+
52+
print("Client: But I can work with it via the Adapter:")
53+
adapter = Adapter()
54+
client_code(adapter)
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
class Target:
2+
"""
3+
The Target defines the domain-specific interface used by the client code.
4+
"""
5+
6+
def request(self) -> str:
7+
return "Target: The default target's behavior."
8+
9+
10+
class Adaptee:
11+
"""
12+
The Adaptee contains some useful behavior, but its interface is incompatible
13+
with the existing client code. The Adaptee needs some adaptation before the
14+
client code can use it.
15+
"""
16+
17+
def specific_request(self) -> str:
18+
return ".eetpadA eht fo roivaheb laicepS"
19+
20+
21+
class Adapter(Target):
22+
"""
23+
The Adapter makes the Adaptee's interface compatible with the Target's
24+
interface via composition.
25+
"""
26+
27+
def __init__(self, adaptee: Adaptee) -> None:
28+
self.adaptee = adaptee
29+
30+
def request(self) -> str:
31+
return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}"
32+
33+
34+
def client_code(target: Target) -> None:
35+
"""
36+
The client code supports all classes that follow the Target interface.
37+
"""
38+
39+
print(target.request(), end="")
40+
41+
42+
if __name__ == "__main__":
43+
print("Client: I can work just fine with the Target objects:")
44+
target = Target()
45+
client_code(target)
46+
print("\n")
47+
48+
adaptee = Adaptee()
49+
print(
50+
"Client: The Adaptee class has a weird interface. "
51+
"See, I don't understand it:"
52+
)
53+
print(f"Adaptee: {adaptee.specific_request()}", end="\n\n")
54+
55+
print("Client: But I can work with it via the Adapter:")
56+
adapter = Adapter(adaptee)
57+
client_code(adapter)

decorator/decorator.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
class Component:
2+
"""
3+
The base Component interface defines operations that can be altered by
4+
decorators.
5+
"""
6+
7+
def operation(self) -> str:
8+
pass
9+
10+
11+
class ConcreteComponent(Component):
12+
"""
13+
Concrete Components provide default implementations of the operations. There
14+
might be several variations of these classes.
15+
"""
16+
17+
def operation(self) -> str:
18+
return "ConcreteComponent"
19+
20+
21+
class Decorator(Component):
22+
"""
23+
The base Decorator class follows the same interface as the other components.
24+
The primary purpose of this class is to define the wrapping interface for
25+
all concrete decorators. The default implementation of the wrapping code
26+
might include a field for storing a wrapped component and the means to
27+
initialize it.
28+
"""
29+
30+
_component: Component = None
31+
32+
def __init__(self, component: Component) -> None:
33+
self._component = component
34+
35+
@property
36+
def component(self) -> Component:
37+
"""
38+
The Decorator delegates all work to the wrapped component.
39+
"""
40+
41+
return self._component
42+
43+
def operation(self) -> str:
44+
return self._component.operation()
45+
46+
47+
class ConcreteDecoratorA(Decorator):
48+
"""
49+
Concrete Decorators call the wrapped object and alter its result in some
50+
way.
51+
"""
52+
53+
def operation(self) -> str:
54+
"""
55+
Decorators may call parent implementation of the operation, instead of
56+
calling the wrapped object directly. This approach simplifies extension
57+
of decorator classes.
58+
"""
59+
return f"ConcreteDecoratorA({self.component.operation()})"
60+
61+
62+
class ConcreteDecoratorB(Decorator):
63+
"""
64+
Decorators can execute their behavior either before or after the call to a
65+
wrapped object.
66+
"""
67+
68+
def operation(self) -> str:
69+
return f"ConcreteDecoratorB({self.component.operation()})"
70+
71+
72+
def client_code(component: Component) -> None:
73+
"""
74+
The client code works with all objects using the Component interface. This
75+
way it can stay independent of the concrete classes of components it works
76+
with.
77+
"""
78+
79+
# ...
80+
81+
print(f"RESULT: {component.operation()}", end="")
82+
83+
# ...
84+
85+
86+
if __name__ == "__main__":
87+
# This way the client code can support both simple components...
88+
simple = ConcreteComponent()
89+
print("Client: I've got a simple component:")
90+
client_code(simple)
91+
print("\n")
92+
93+
# ...as well as decorated ones.
94+
#
95+
# Note how decorators can wrap not only simple components but the other
96+
# decorators as well.
97+
decorator1 = ConcreteDecoratorA(simple)
98+
decorator2 = ConcreteDecoratorB(decorator1)
99+
print("Client: Now I've got a decorated component:")
100+
client_code(decorator2)

facade/facade.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from __future__ import annotations
2+
3+
4+
class Facade:
5+
"""
6+
The Facade class provides a simple interface to the complex logic of one or
7+
several subsystems. The Facade delegates the client requests to the
8+
appropriate objects within the subsystem. The Facade is also responsible for
9+
managing their lifecycle. All of this shields the client from the undesired
10+
complexity of the subsystem.
11+
"""
12+
13+
def __init__(self, subsystem1: Subsystem1, subsystem2: Subsystem2) -> None:
14+
"""
15+
Depending on your application's needs, you can provide the Facade with
16+
existing subsystem objects or force the Facade to create them on its
17+
own.
18+
"""
19+
20+
self._subsystem1 = subsystem1 or Subsystem1()
21+
self._subsystem2 = subsystem2 or Subsystem2()
22+
23+
def operation(self) -> str:
24+
"""
25+
The Facade's methods are convenient shortcuts to the sophisticated
26+
functionality of the subsystems. However, clients get only to a fraction
27+
of a subsystem's capabilities.
28+
"""
29+
30+
results = []
31+
results.append("Facade initializes subsystems:")
32+
results.append(self._subsystem1.operation1())
33+
results.append(self._subsystem2.operation1())
34+
results.append("Facade orders subsystems to perform the action:")
35+
results.append(self._subsystem1.operation_n())
36+
results.append(self._subsystem2.operation_z())
37+
return "\n".join(results)
38+
39+
40+
class Subsystem1:
41+
"""
42+
The Subsystem can accept requests either from the facade or client directly.
43+
In any case, to the Subsystem, the Facade is yet another client, and it's
44+
not a part of the Subsystem.
45+
"""
46+
47+
def operation1(self) -> str:
48+
return "Subsystem1: Ready!"
49+
50+
# ...
51+
52+
def operation_n(self) -> str:
53+
return "Subsystem1: Go!"
54+
55+
56+
class Subsystem2:
57+
"""
58+
Some facades can work with multiple subsystems at the same time.
59+
"""
60+
61+
def operation1(self) -> str:
62+
return "Subsystem2: Get ready!"
63+
64+
# ...
65+
66+
def operation_z(self) -> str:
67+
return "Subsystem2: Fire!"
68+
69+
70+
def client_code(facade: Facade) -> None:
71+
"""
72+
The client code works with complex subsystems through a simple interface
73+
provided by the Facade. When a facade manages the lifecycle of the
74+
subsystem, the client might not even know about the existence of the
75+
subsystem. This approach lets you keep the complexity under control.
76+
"""
77+
78+
print(facade.operation(), end="")
79+
80+
81+
if __name__ == "__main__":
82+
# The client code may have some of the subsystem's objects already created.
83+
# In this case, it might be worthwhile to initialize the Facade with these
84+
# objects instead of letting the Facade create new instances.
85+
subsystem1 = Subsystem1()
86+
subsystem2 = Subsystem2()
87+
facade = Facade(subsystem1, subsystem2)
88+
client_code(facade)

0 commit comments

Comments
 (0)