Design Patterns Cheat Sheet
Design patterns are proven solutions to common problems in software design. They are templates designed to help developers solve recurring problems and create more flexible and reusable code. Here's a quick reference to some of the most widely used design patterns, categorized into three groups: Creational, Structural, and Behavioral.
Creational Patterns
Singleton
- Purpose: Ensure a class has only one instance and provide a global point of access to it.
- Use Case: Configuration classes, logging, thread pools.
Example:
class Singleton: _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance
Factory Method
- Purpose: Define an interface for creating an object but let subclasses alter the type of objects that will be created.
- Use Case: Creating objects without specifying the exact class.
Example:
class Creator: def factory_method(self): raise NotImplementedError class ConcreteCreator(Creator): def factory_method(self): return ConcreteProduct()
Abstract Factory
- Purpose: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
- Use Case: Creating related objects without specifying their classes.
Example:
class AbstractFactory: def create_product_a(self): pass def create_product_b(self): pass class ConcreteFactory1(AbstractFactory): def create_product_a(self): return ProductA1() def create_product_b(self): return ProductB1()
Builder
- Purpose: Separate the construction of a complex object from its representation, allowing the same construction process to create various representations.
- Use Case: Constructing complex objects step by step.
Example:
class Builder: def build_part(self): pass class ConcreteBuilder(Builder): def build_part(self): return Product()
Prototype
- Purpose: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
- Use Case: When the cost of creating a new object is expensive.
Example:
import copy class Prototype: def clone(self): return copy.deepcopy(self)
Structural Patterns
Adapter
- Purpose: Convert the interface of a class into another interface that clients expect.
- Use Case: Integrating new functionality with legacy code.
Example:
class Target: def request(self): pass class Adaptee: def specific_request(self): pass class Adapter(Target): def __init__(self, adaptee): self.adaptee = adaptee def request(self): self.adaptee.specific_request()
Bridge
- Purpose: Separate an object’s interface from its implementation so the two can vary independently.
- Use Case: Avoiding permanent binding between abstraction and implementation.
Example:
class Abstraction: def __init__(self, implementor): self.implementor = implementor def operation(self): self.implementor.operation_impl() class Implementor: def operation_impl(self): pass
Composite
- Purpose: Compose objects into tree structures to represent part-whole hierarchies.
- Use Case: Treating individual objects and compositions of objects uniformly.
Example:
class Component: 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()
Decorator
- Purpose: Attach additional responsibilities to an object dynamically.
- Use Case: Adding behavior to objects at runtime.
Example:
class Component: def operation(self): pass class Decorator(Component): def __init__(self, component): self.component = component def operation(self): self.component.operation() class ConcreteDecorator(Decorator): def operation(self): super().operation() # Additional behavior
Facade
- Purpose: Provide a simplified interface to a complex subsystem.
- Use Case: Simplifying interactions with complex systems.
Example:
class Facade: def __init__(self): self.subsystem1 = Subsystem1() self.subsystem2 = Subsystem2() def operation(self): self.subsystem1.operation1() self.subsystem2.operation2()
Flyweight
- Purpose: Use sharing to support a large number of fine-grained objects efficiently.
- Use Case: Reducing memory usage with a large number of similar objects.
Example:
class Flyweight: def __init__(self, intrinsic_state): self.intrinsic_state = intrinsic_state class FlyweightFactory: def __init__(self): self.flyweights = {} def get_flyweight(self, key): if key not in self.flyweights: self.flyweights[key] = Flyweight(key) return self.flyweights[key]
Proxy
- Purpose: Provide a surrogate or placeholder for another object to control access to it.
- Use Case: Controlling access to objects, lazy initialization, access control.
Example:
class Subject: def request(self): pass class RealSubject(Subject): def request(self): pass class Proxy(Subject): def __init__(self, real_subject): self.real_subject = real_subject def request(self): # Access control or additional behavior self.real_subject.request()
Behavioral Patterns
Chain of Responsibility
- Purpose: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.
- Use Case: Decoupling request senders and receivers.
Example:
class Handler: def __init__(self, successor=None): self.successor = successor def handle_request(self, request): if self.successor: self.successor.handle_request(request)
Command
- Purpose: Encapsulate a request as an object, thereby allowing parameterization of clients with queues, requests, and operations.
- Use Case: Implementing undo/redo functionality.
Example:
class Command: def execute(self): pass class ConcreteCommand(Command): def __init__(self, receiver): self.receiver = receiver def execute(self): self.receiver.action() class Receiver: def action(self): pass
Interpreter
- Purpose: Define a grammar for a language and provide an interpreter to deal with this grammar.
- Use Case: Parsing and interpreting expressions.
Example:
class AbstractExpression: def interpret(self, context): pass class TerminalExpression(AbstractExpression): def interpret(self, context): pass class NonterminalExpression(AbstractExpression): def __init__(self, expressions): self.expressions = expressions def interpret(self, context): for expr in self.expressions: expr.interpret(context)
Iterator
- Purpose: Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
- Use Case: Traversing collections like lists and trees.
Example:
class Iterator: def __init__(self, collection): self.collection = collection self.index = 0 def has_next(self): return self.index < len(self.collection) def next(self): item = self.collection[self.index] self.index += 1 return item
Mediator
- Purpose: Define an object that encapsulates how a set of objects interact, promoting loose coupling.
- Use Case: Reducing complexity in communication between multiple objects.
Example:
class Mediator: def notify(self, sender, event): pass class ConcreteMediator(Mediator): def notify(self, sender, event): pass class Colleague: def __init__(self, mediator): self.mediator = mediator def notify_mediator(self, event): self.mediator.notify(self, event)
Memento
- Purpose: Capture and externalize an object's internal state so that it can be restored later without violating encapsulation.
- Use Case: Implementing undo/redo functionality.
- Example: ```python
class Memento: def init(self, state): self.state = state
class Originator: def set_memento(self, memento): self.state = memento.state
def create_memento(self): return Memento(self.state)
7. **Observer**
- **Purpose**: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
- **Use Case**: Implementing event handling systems.
- **Example**:
```python
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()
class Observer:
def update(self):
pass
State
- Purpose: Allow an object to alter its behavior when its internal state changes, appearing to change its class.
- Use Case: Implementing state machines.
Example:
class State: def handle(self, context): pass class ConcreteStateA(State): def handle(self, context): context.state = ConcreteStateB() class Context: def __init__(self, state): self.state = state def request(self): self.state.handle(self)
Strategy
- Purpose: Define a family of algorithms, encapsulate each one, and make them interchangeable.
- Use Case: Implementing interchangeable algorithms or behaviors.
Example:
class Strategy: def execute(self): pass class ConcreteStrategyA(Strategy): def execute(self): pass class Context: def __init__(self, strategy): self.strategy = strategy def execute_strategy(self): self.strategy.execute()
Template Method
- Purpose: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
- Use Case: Implementing invariant parts of an algorithm once and allowing subclasses to refine variable parts.
Example:
class AbstractClass: def template_method(self): self.step1() self.step2() def step1(self): pass def step2(self): pass class ConcreteClass(AbstractClass): def step1(self): pass def step2(self): pass
Visitor
- Purpose: Represent an operation to be performed on the elements of an object structure without changing the classes of the elements.
- Use Case: Adding operations to class hierarchies without modifying the classes.
Example:
class Visitor: def visit(self, element): pass class ConcreteVisitor(Visitor): def visit(self, element): pass class Element: def accept(self, visitor): visitor.visit(self)
Summary
- Creational Patterns: Focus on object creation mechanisms.
- Structural Patterns: Focus on object composition or structure.
- Behavioral Patterns: Focus on object interaction and responsibility.
This cheat sheet provides a high-level overview of the most common design patterns. For deeper understanding and practical implementation, refer to resources like the Design Patterns: Elements of Reusable Object-Oriented Software
book (the Gang of Four
book) and other detailed design pattern references.