Skip to content

Commit 5e6d04a

Browse files
docs: add design-patterns
1 parent d3ba5c6 commit 5e6d04a

2 files changed

Lines changed: 286 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Factory Pattern
2+
3+
`people.csv`
4+
5+
``` csv
6+
Alice Smith,60,Harvard University,History
7+
Michael Smith,27,Princeton University,Mechanical Engineering
8+
Bob Davis,43,Yale University,Biology
9+
```
10+
11+
``` python
12+
class Person:
13+
def __init__(self, name, age, university, degree):
14+
self.name = name.title()
15+
self.age = age
16+
self.university = university.title()
17+
self.degree = degree.capitalize()
18+
19+
@classmethod
20+
def from_csv_line(cls, line: str) -> "Person":
21+
return cls(*line.strip().split(","))
22+
23+
24+
with open("people.csv", "r") as rf:
25+
lines = rf.readlines()
26+
27+
people = map(Person.from_csv_line, lines)
28+
29+
for person in people:
30+
print(
31+
f"{person.name} is {person.age},"
32+
f"studying {person.degree} at {person.university}."
33+
)
34+
```
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Overview
2+
3+
## References
4+
5+
[python-patterns](https://python-patterns.guide/)
6+
[python-parameterized-design-patterns](https://www.toptal.com/python/python-parameterized-design-patterns)
7+
8+
## Overview & examples
9+
10+
Design patterns are common solutions to recurring problems in software design. They provide a standard terminology and are specific to particular scenarios. Here’s an overview of some key design patterns implemented in Python:
11+
12+
### 1. **Singleton Pattern**
13+
The Singleton ensures a class has only one instance and provides a global point of access to it. This is useful for scenarios like database connections or configurations where multiple instances would cause issues.
14+
15+
**Python Example:**
16+
```python
17+
class Singleton:
18+
_instance = None
19+
20+
def __new__(cls):
21+
if cls._instance is None:
22+
cls._instance = super(Singleton, cls).__new__(cls)
23+
return cls._instance
24+
25+
# Usage
26+
singleton1 = Singleton()
27+
singleton2 = Singleton()
28+
29+
print(singleton1 == singleton2) # Output: True
30+
```
31+
32+
### 2. **Factory Pattern**
33+
The Factory pattern provides an interface for creating objects, but allows subclasses to alter the type of objects that will be created. This decouples object creation from its implementation.
34+
35+
**Python Example:**
36+
```python
37+
class Animal:
38+
def speak(self):
39+
pass
40+
41+
class Dog(Animal):
42+
def speak(self):
43+
return "Woof"
44+
45+
class Cat(Animal):
46+
def speak(self):
47+
return "Meow"
48+
49+
class AnimalFactory:
50+
@staticmethod
51+
def get_animal(animal_type):
52+
if animal_type == "dog":
53+
return Dog()
54+
elif animal_type == "cat":
55+
return Cat()
56+
return None
57+
58+
# Usage
59+
animal = AnimalFactory.get_animal("dog")
60+
print(animal.speak()) # Output: Woof
61+
```
62+
63+
### 3. **Observer Pattern**
64+
The Observer pattern defines an one-to-many relationship between objects, where a change in one object leads to an update in others. It is often used in event handling systems.
65+
66+
**Python Example:**
67+
```python
68+
class Subject:
69+
def __init__(self):
70+
self._observers = []
71+
72+
def register(self, observer):
73+
self._observers.append(observer)
74+
75+
def notify_all(self, message):
76+
for observer in self._observers:
77+
observer.update(message)
78+
79+
class Observer:
80+
def update(self, message):
81+
pass
82+
83+
class ConcreteObserver(Observer):
84+
def update(self, message):
85+
print(f"Observer received message: {message}")
86+
87+
# Usage
88+
subject = Subject()
89+
observer1 = ConcreteObserver()
90+
observer2 = ConcreteObserver()
91+
92+
subject.register(observer1)
93+
subject.register(observer2)
94+
95+
subject.notify_all("Pattern design is fun!") # Both observers receive the message
96+
```
97+
98+
### 4. **Decorator Pattern**
99+
The Decorator pattern adds new functionality to an existing object dynamically. This is especially useful for adhering to the Open-Closed Principle (open for extension, closed for modification).
100+
101+
**Python Example:**
102+
```python
103+
def make_bold(func):
104+
def wrapper():
105+
return "<b>" + func() + "</b>"
106+
return wrapper
107+
108+
def make_italic(func):
109+
def wrapper():
110+
return "<i>" + func() + "</i>"
111+
return wrapper
112+
113+
@make_bold
114+
@make_italic
115+
def greet():
116+
return "Hello"
117+
118+
# Usage
119+
print(greet()) # Output: <b><i>Hello</i></b>
120+
```
121+
122+
### 5. **Strategy Pattern**
123+
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.
124+
125+
**Python Example:**
126+
```python
127+
class Strategy:
128+
def execute(self, data):
129+
pass
130+
131+
class ConcreteStrategyA(Strategy):
132+
def execute(self, data):
133+
return sorted(data)
134+
135+
class ConcreteStrategyB(Strategy):
136+
def execute(self, data):
137+
return sorted(data, reverse=True)
138+
139+
class Context:
140+
def __init__(self, strategy: Strategy):
141+
self.strategy = strategy
142+
143+
def set_strategy(self, strategy: Strategy):
144+
self.strategy = strategy
145+
146+
def execute_strategy(self, data):
147+
return self.strategy.execute(data)
148+
149+
# Usage
150+
data = [5, 3, 8, 1]
151+
context = Context(ConcreteStrategyA())
152+
print(context.execute_strategy(data)) # Output: [1, 3, 5, 8]
153+
154+
context.set_strategy(ConcreteStrategyB())
155+
print(context.execute_strategy(data)) # Output: [8, 5, 3, 1]
156+
```
157+
158+
### 6. **Adapter Pattern**
159+
The Adapter pattern allows incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces.
160+
161+
**Python Example:**
162+
```python
163+
class EuropeanSocket:
164+
def voltage(self):
165+
return 230
166+
167+
def live(self):
168+
return 1
169+
170+
def neutral(self):
171+
return -1
172+
173+
class USASocket:
174+
def voltage(self):
175+
return 120
176+
177+
def live(self):
178+
return 1
179+
180+
def neutral(self):
181+
return 0
182+
183+
class SocketAdapter:
184+
def __init__(self, usa_socket):
185+
self.usa_socket = usa_socket
186+
187+
def voltage(self):
188+
return self.usa_socket.voltage()
189+
190+
def live(self):
191+
return self.usa_socket.live()
192+
193+
def neutral(self):
194+
return self.usa_socket.neutral()
195+
196+
# Usage
197+
usa_socket = USASocket()
198+
adapter = SocketAdapter(usa_socket)
199+
200+
print(f"Adapter voltage: {adapter.voltage()}V") # Output: Adapter voltage: 120V
201+
```
202+
203+
### 7. **Command Pattern**
204+
The Command pattern turns a request into a stand-alone object that contains all information about the request. This is useful for undo functionality, queues of operations, etc.
205+
206+
**Python Example:**
207+
```python
208+
class Command:
209+
def execute(self):
210+
pass
211+
212+
class LightOnCommand(Command):
213+
def __init__(self, light):
214+
self.light = light
215+
216+
def execute(self):
217+
self.light.on()
218+
219+
class LightOffCommand(Command):
220+
def __init__(self, light):
221+
self.light = light
222+
223+
def execute(self):
224+
self.light.off()
225+
226+
class Light:
227+
def on(self):
228+
print("Light is ON")
229+
230+
def off(self):
231+
print("Light is OFF")
232+
233+
class RemoteControl:
234+
def __init__(self, command: Command):
235+
self.command = command
236+
237+
def press(self):
238+
self.command.execute()
239+
240+
# Usage
241+
light = Light()
242+
light_on = LightOnCommand(light)
243+
light_off = LightOffCommand(light)
244+
245+
remote = RemoteControl(light_on)
246+
remote.press() # Output: Light is ON
247+
248+
remote = RemoteControl(light_off)
249+
remote.press() # Output: Light is OFF
250+
```
251+
252+
These patterns are widely used to solve design problems and make systems more maintainable, extensible, and scalable. They encapsulate best practices that help developers tackle common software design challenges.

0 commit comments

Comments
 (0)