What Are Design Patterns?¶
Design patterns are reusable solutions to commonly occurring problems in software design. They are not finished code that can be directly transformed into source code but rather templates for how to solve problems in different situations.
The concept was popularized by the “Gang of Four” (GoF) in their 1994 book Design Patterns: Elements of Reusable Object-Oriented Software.
“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem.” — Christopher Alexander
Categories of Design Patterns¶
Design patterns are typically divided into three categories:
| Category | Purpose | Examples |
|---|---|---|
| Creational | Object creation mechanisms | Singleton, Factory, Builder |
| Structural | Object composition | Adapter, Decorator, Facade |
| Behavioral | Object communication | Observer, Strategy, Command |
The Singleton Pattern¶
The Singleton pattern ensures a class has only one instance and provides a global point of access to it.
When to Use¶
- Database connections
- Configuration managers
- Logging services
- Thread pools
Implementation in Python¶
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
# Usage
config1 = Singleton()
config2 = Singleton()
assert config1 is config2 # True - same instance
Implementation in C¶
public sealed class Singleton
{
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
private Singleton() { }
public static Singleton Instance => _instance.Value;
}
The Observer Pattern¶
The Observer pattern defines a one-to-many dependency between objects. When one object (the subject) changes state, all its dependents (observers) are notified.
Real-World Analogy¶
Think of a newspaper subscription: the publisher (subject) sends newspapers to subscribers (observers) whenever a new edition is released.
Implementation in Python¶
from abc import ABC, abstractmethod
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self, data):
for observer in self._observers:
observer.update(data)
class Observer(ABC):
@abstractmethod
def update(self, data):
pass
class PriceAlert(Observer):
def __init__(self, name):
self.name = name
def update(self, data):
print(f"[{self.name}] Price updated: {data}")
# Usage
market = Subject()
trader = PriceAlert("Trading Desk")
risk = PriceAlert("Risk Management")
market.attach(trader)
market.attach(risk)
market.notify({"AAPL": 150.25})
The Strategy Pattern¶
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable.
Use Case in Finance¶
Different pricing models for financial instruments:
from abc import ABC, abstractmethod
class PricingStrategy(ABC):
@abstractmethod
def calculate_price(self, instrument):
pass
class BlackScholes(PricingStrategy):
def calculate_price(self, instrument):
# Black-Scholes pricing model
return "Price via Black-Scholes"
class MonteCarlo(PricingStrategy):
def calculate_price(self, instrument):
# Monte Carlo simulation
return "Price via Monte Carlo"
class PricingEngine:
def __init__(self, strategy: PricingStrategy):
self._strategy = strategy
def price(self, instrument):
return self._strategy.calculate_price(instrument)
# Usage - easily swap strategies
engine = PricingEngine(BlackScholes())
print(engine.price("EUR/USD Option"))
engine = PricingEngine(MonteCarlo())
print(engine.price("Exotic Derivative"))
Key Takeaways¶
- Don’t over-engineer: Use patterns when they solve a real problem, not to show off
- Know the trade-offs: Each pattern has pros and cons
- Composition over inheritance: Most modern patterns favor composition
- SOLID principles: Design patterns often implement SOLID principles naturally
Further Reading¶
- Design Patterns: Elements of Reusable Object-Oriented Software by GoF
- Head First Design Patterns by Eric Freeman
- Refactoring to Patterns by Joshua Kerievsky