Patterns that deal with object creation mechanisms
Ensures a class has only one instance and provides a global access point to it.
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceUse when: You need exactly one instance (database connections, loggers, configuration managers).
Defines an interface for creating objects but lets subclasses decide which class to instantiate.
from abc import ABC, abstractmethod
class Creator(ABC):
@abstractmethod
def factory_method(self):
pass
def operation(self):
product = self.factory_method()
return product.use()Use when: You don't know the exact types of objects your code should work with ahead of time.
Provides an interface for creating families of related objects without specifying their concrete classes.
class GUIFactory(ABC):
@abstractmethod
def create_button(self): pass
@abstractmethod
def create_checkbox(self): passUse when: Your system needs to work with multiple families of related products.
Separates complex object construction from its representation, allowing step-by-step construction.
class QueryBuilder:
def __init__(self):
self.query = {}
def select(self, fields):
self.query['select'] = fields
return self
def where(self, condition):
self.query['where'] = condition
return self
def build(self):
return Query(self.query)Use when: Constructing complex objects with many optional parameters or steps.
Creates new objects by copying existing instances (cloning).
import copy
class Prototype:
def clone(self):
return copy.deepcopy(self)Use when: Object creation is expensive or complex, and you want to copy existing instances.
Patterns that deal with object composition and relationships
Converts one interface into another that clients expect, allowing incompatible interfaces to work together.
class EuropeanSocket:
def voltage(self): return 230
class USASocketAdapter:
def __init__(self, socket):
self.socket = socket
def voltage(self):
return 110 # Convert from 230VUse when: You need to integrate classes with incompatible interfaces.
Separates abstraction from implementation so they can vary independently.
class Device(ABC):
@abstractmethod
def turn_on(self): pass
class RemoteControl:
def __init__(self, device: Device):
self.device = device
def power(self):
self.device.turn_on()Use when: You want to avoid permanent binding between abstraction and implementation.
Composes objects into tree structures to represent part-whole hierarchies, treating individual objects and compositions uniformly.
class Component(ABC):
@abstractmethod
def operation(self): pass
class Composite(Component):
def __init__(self):
self.children = []
def add(self, component):
self.children.append(component)
def operation(self):
for child in self.children:
child.operation()Use when: You need to represent hierarchies of objects (file systems, UI components, org charts).
Adds new functionality to objects dynamically without altering their structure.
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_calls
def process_data(data):
return dataUse when: You need to add responsibilities to objects dynamically and transparently.
Provides a simplified interface to a complex subsystem.
class ComputerFacade:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.disk = HardDrive()
def start(self):
self.cpu.freeze()
self.memory.load()
self.cpu.execute()Use when: You want to provide a simple interface to a complex system.
Shares common state between multiple objects to save memory.
class CharacterFlyweight:
_characters = {}
@classmethod
def get_character(cls, char):
if char not in cls._characters:
cls._characters[char] = Character(char)
return cls._characters[char]Use when: You need to support large numbers of similar objects efficiently.
Provides a surrogate or placeholder to control access to another object.
class ImageProxy:
def __init__(self, filename):
self.filename = filename
self._image = None
def display(self):
if self._image is None:
self._image = RealImage(self.filename)
self._image.display()Use when: You need lazy initialization, access control, or remote object representation.
Patterns that deal with object collaboration and responsibility distribution
Passes requests along a chain of handlers where each handler decides to process or pass the request.
class Handler(ABC):
def __init__(self, successor=None):
self.successor = successor
def handle(self, request):
if self.successor:
return self.successor.handle(request)Use when: Multiple objects can handle a request and the handler isn't known beforehand.
Encapsulates a request as an object, allowing parameterization and queuing of requests.
class Command(ABC):
@abstractmethod
def execute(self): pass
class SaveCommand(Command):
def __init__(self, document):
self.document = document
def execute(self):
self.document.save()Use when: You need to parameterize objects with operations, queue operations, or support undo.
Provides sequential access to elements without exposing underlying representation.
class Iterator(ABC):
@abstractmethod
def __next__(self): pass
class BookIterator:
def __init__(self, books):
self.books = books
self.index = 0
def __next__(self):
if self.index < len(self.books):
result = self.books[self.index]
self.index += 1
return result
raise StopIterationUse when: You need to traverse collections without exposing their internal structure.
Defines an object that encapsulates how objects interact, promoting loose coupling.
class ChatMediator:
def __init__(self):
self.users = []
def send_message(self, message, sender):
for user in self.users:
if user != sender:
user.receive(message)Use when: Object interactions are complex and you want to centralize communication logic.
Captures and externalizes an object's internal state for later restoration without violating encapsulation.
class Memento:
def __init__(self, state):
self._state = state
def get_state(self):
return self._state
class Editor:
def save(self):
return Memento(self.text)
def restore(self, memento):
self.text = memento.get_state()Use when: You need to implement undo/redo functionality or snapshots.
Defines a one-to-many dependency where when one object changes state, all dependents are notified.
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def notify(self):
for observer in self._observers:
observer.update(self)Use when: Changes to one object require changing others, and you don't know how many objects need to change.
Allows an object to alter its behavior when its internal state changes.
class State(ABC):
@abstractmethod
def handle(self, context): pass
class Context:
def __init__(self, state):
self._state = state
def request(self):
self._state.handle(self)Use when: Object behavior depends on its state and must change at runtime.
Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
class SortStrategy(ABC):
@abstractmethod
def sort(self, data): pass
class QuickSort(SortStrategy):
def sort(self, data):
# Quick sort implementation
pass
class Sorter:
def __init__(self, strategy: SortStrategy):
self.strategy = strategy
def sort(self, data):
return self.strategy.sort(data)Use when: You have multiple algorithms for a task and want to switch between them at runtime.
Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps.
class DataProcessor(ABC):
def process(self):
self.read_data()
self.process_data()
self.save_data()
@abstractmethod
def read_data(self): pass
@abstractmethod
def process_data(self): passUse when: You want to define the overall structure of an algorithm while letting subclasses customize certain steps.
Separates algorithms from the objects they operate on, allowing new operations without modifying classes.
class Visitor(ABC):
@abstractmethod
def visit(self, element): pass
class Element(ABC):
@abstractmethod
def accept(self, visitor): passUse when: You need to perform operations across a heterogeneous collection of objects.
Need to create objects? → Creational Patterns
Need to compose objects? → Structural Patterns
Need objects to communicate? → Behavioral Patterns
Most commonly used in modern Python: Singleton, Factory, Decorator, Observer, Strategy, Command
Python-specific considerations: Many patterns are simplified in Python due to first-class functions, decorators, and duck typing.