
Design Patterns
The Ultimate Guide to Design Patterns
1. From Zero to Hero: Understanding Design Patterns Through Analogies, Examples & Code
2. Table of Contents
- What Are Design Patterns?
- Why Should You Care?
- Creational Patterns (5 patterns)
- Structural Patterns (7 patterns)
- Behavioral Patterns (11 patterns)
- Additional Modern Patterns
- Quick Reference & Mnemonics
3. What Are Design Patterns?
Design patterns are reusable solutions to commonly occurring problems in software design. They're not finished code you can copy-paste — they're templates, blueprints, or recipes that guide you in solving specific design challenges.
Think of them like architectural blueprints: just as architects don't redesign doors and windows for every building, software developers don't need to reinvent solutions for recurring problems.
3.1. The Gang of Four (GoF)
In 1994, four authors — Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides — published "Design Patterns: Elements of Reusable Object-Oriented Software." This book cataloged 23 patterns that have become the foundation of modern software design.
4. Why Should You Care?
4.1. Without Design Patterns:
- 🔴 Code becomes tangled spaghetti over time
- 🔴 Every developer solves the same problem differently
- 🔴 Maintenance nightmares — changing one thing breaks ten others
- 🔴 Onboarding new team members takes forever
- 🔴 Reinventing wheels constantly
4.2. With Design Patterns:
- 🟢 Common vocabulary across teams and projects
- 🟢 Proven solutions backed by decades of use
- 🟢 Flexible, maintainable, scalable code
- 🟢 Faster development through recognized templates
- 🟢 Easier code reviews and documentation
5. Rating System
Each pattern is rated on:
| Metric | Description |
|---|---|
| Complexity | How difficult to understand and implement (★☆☆☆☆ = Simple, ★★★★★ = Complex) |
| Frequency | How often you'll use this in real projects (★☆☆☆☆ = Rare, ★★★★★ = Very Common) |
Creational Patterns
Mnemonic: "Smart Factories Always Build Prototypes"
Singleton, Factory Method, Abstract Factory, Builder, Prototype
Creational patterns deal with object creation mechanisms — trying to create objects in a manner suitable to the situation, rather than using basic instantiation.
6. 1. Singleton
Complexity: ★★☆☆☆ | Frequency: ★★★★★
6.1. Definition
Singleton ensures a class has only one instance and provides a global point of access to it.
6.2. Why Is It Required?
Some resources should exist exactly once: a database connection pool, a logger, a configuration manager, or a game's audio system. Creating multiple instances would waste resources, cause conflicts, or produce inconsistent behavior.
6.3. With vs Without
| Without Singleton | With Singleton |
|---|---|
| Multiple database connections fighting for resources | Single shared connection pool |
| Inconsistent configuration across app modules | One source of truth for settings |
| Multiple loggers writing to same file = corruption | Unified logging system |
6.4. Best Use Cases
- Web Dev: Application configuration, logging service, database connection pool
- Game Dev: Audio manager, game state manager, input handler
- Enterprise: License manager, thread pool, cache manager
6.5. Analogy
The President of a Country — There can only be one president at a time. No matter who asks "Who is the president?", they get the same person. You can't just create a new president; you access the existing one through official channels.
6.6. Code Examples
Python:
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = cls._connect_to_db()
return cls._instance
@staticmethod
def _connect_to_db():
print("Establishing database connection...")
return {"host": "localhost", "port": 5432}
# Usage
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True — same instance!
C++:
#include <iostream>
#include <mutex>
class DatabaseConnection {
private:
static DatabaseConnection* instance;
static std::mutex mutex;
DatabaseConnection() {
std::cout << "Establishing database connection..." << std::endl;
}
public:
DatabaseConnection(const DatabaseConnection&) = delete;
static DatabaseConnection* getInstance() {
std::lock_guard<std::mutex> lock(mutex);
if (instance == nullptr) {
instance = new DatabaseConnection();
}
return instance;
}
};
DatabaseConnection* DatabaseConnection::instance = nullptr;
std::mutex DatabaseConnection::mutex;
JavaScript:
class DatabaseConnection {
constructor() {
if (DatabaseConnection.instance) {
return DatabaseConnection.instance;
}
this.connection = { host: "localhost", port: 5432 };
DatabaseConnection.instance = this;
}
}
const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
7. 2. Factory Method
Complexity: ★★☆☆☆ | Frequency: ★★★★★
7.1. Definition
Factory Method defines an interface for creating objects but lets subclasses decide which class to instantiate.
7.2. Why Is It Required?
When your code needs to work with various types of objects but shouldn't know the exact classes in advance. It decouples the client code from the concrete classes it needs to instantiate.
7.3. With vs Without
| Without Factory Method | With Factory Method |
|---|---|
if-else chains everywhere for object creation | Clean, extensible creation logic |
| Adding new types requires modifying existing code | Add new types by adding new classes |
7.4. Best Use Cases
- Web Dev: Payment gateway selection, UI component creation
- Game Dev: Spawning different enemy types, platform-specific renderers
- Enterprise: Document generators (PDF, Word, Excel), notification services
7.5. Analogy
A Logistics Company — A logistics company can deliver packages, but the actual transport (truck, ship, plane) depends on the destination. The company doesn't care how; it just calls "deliver()" and the appropriate transport handles it.
7.6. Code Examples
Python:
from abc import ABC, abstractmethod
class Notification(ABC):
@abstractmethod
def send(self, message: str) -> None:
pass
class EmailNotification(Notification):
def send(self, message: str) -> None:
print(f"📧 Sending Email: {message}")
class SMSNotification(Notification):
def send(self, message: str) -> None:
print(f"📱 Sending SMS: {message}")
class NotificationFactory(ABC):
@abstractmethod
def create_notification(self) -> Notification:
pass
def notify(self, message: str) -> None:
notification = self.create_notification()
notification.send(message)
class EmailFactory(NotificationFactory):
def create_notification(self) -> Notification:
return EmailNotification()
# Usage
EmailFactory().notify("Your order has shipped!")
C++:
class Notification {
public:
virtual void send(const std::string& message) = 0;
};
class EmailNotification : public Notification {
public:
void send(const std::string& message) override {
std::cout << "📧 Email: " << message << std::endl;
}
};
class NotificationFactory {
public:
virtual std::unique_ptr<Notification> createNotification() = 0;
void notify(const std::string& message) {
auto notification = createNotification();
notification->send(message);
}
};
class EmailFactory : public NotificationFactory {
public:
std::unique_ptr<Notification> createNotification() override {
return std::make_unique<EmailNotification>();
}
};
JavaScript:
class Notification {
send(message) { throw new Error("Must implement"); }
}
class EmailNotification extends Notification {
send(message) { console.log(`📧 Email: ${message}`); }
}
class SMSNotification extends Notification {
send(message) { console.log(`📱 SMS: ${message}`); }
}
class NotificationFactory {
createNotification() { throw new Error("Must implement"); }
notify(message) {
const notification = this.createNotification();
notification.send(message);
}
}
class EmailFactory extends NotificationFactory {
createNotification() { return new EmailNotification(); }
}
new EmailFactory().notify("Your order shipped!");
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
8. 3. Abstract Factory
Complexity: ★★★☆☆ | Frequency: ★★★★☆
8.1. Definition
Abstract Factory provides an interface for creating families of related objects without specifying concrete classes. It's a factory of factories.
8.2. Why Is It Required?
When your system needs to work with multiple families of related products, ensuring products from one family aren't mixed with another.
8.3. With vs Without
| Without Abstract Factory | With Abstract Factory |
|---|---|
| Risk of mixing incompatible components | Guaranteed family consistency |
| N × M classes for N abstractions × M implementations | N + M classes total |
8.4. Best Use Cases
- Web Dev: UI theme systems (Light/Dark mode), cross-platform UI
- Game Dev: Platform-specific game assets (PC, Console, Mobile)
- Enterprise: Multi-database support, multi-cloud configurations
8.5. Analogy
IKEA Furniture Collections — IKEA sells complete furniture collections (Modern, Victorian, Minimalist). Each collection has chairs, tables, sofas that match perfectly. You pick a collection, and everything from it looks good together.
8.6. Code Examples
Python:
from abc import ABC, abstractmethod
class Button(ABC):
@abstractmethod
def render(self) -> str:
pass
class Checkbox(ABC):
@abstractmethod
def render(self) -> str:
pass
class LightButton(Button):
def render(self) -> str:
return "[ Light Button ]"
class DarkButton(Button):
def render(self) -> str:
return "[ Dark Button ]"
class UIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass
@abstractmethod
def create_checkbox(self) -> Checkbox:
pass
class LightThemeFactory(UIFactory):
def create_button(self) -> Button:
return LightButton()
def create_checkbox(self) -> Checkbox:
return LightCheckbox()
class DarkThemeFactory(UIFactory):
def create_button(self) -> Button:
return DarkButton()
def create_checkbox(self) -> Checkbox:
return DarkCheckbox()
# Usage
def render_ui(factory: UIFactory):
button = factory.create_button()
print(button.render())
render_ui(DarkThemeFactory())
C++:
class UIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
};
class LightThemeFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<LightButton>();
}
std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<LightCheckbox>();
}
};
JavaScript:
class UIFactory {
createButton() { throw new Error("Must implement"); }
createCheckbox() { throw new Error("Must implement"); }
}
class LightThemeFactory extends UIFactory {
createButton() { return new LightButton(); }
createCheckbox() { return new LightCheckbox(); }
}
class DarkThemeFactory extends UIFactory {
createButton() { return new DarkButton(); }
createCheckbox() { return new DarkCheckbox(); }
}
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
9. 4. Builder
Complexity: ★★★☆☆ | Frequency: ★★★★☆
9.1. Definition
Builder separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
9.2. Why Is It Required?
When creating an object requires many steps, optional parameters, or multiple configurations. Avoids "telescoping constructors" with many parameters.
9.3. With vs Without
| Without Builder | With Builder |
|---|---|
new Pizza(true, false, true, 2, "large") — cryptic | Pizza.builder().cheese().pepperoni().large().build() — clear |
| Many constructor overloads for optional params | Handle any combination cleanly |
9.4. Best Use Cases
- Web Dev: Building HTTP requests, SQL query builders, form builders
- Game Dev: Character creation systems, level builders
- Enterprise: Report generators, configuration objects
9.5. Analogy
Ordering a Custom Burger — At a build-your-own burger restaurant, you say: "beef patty, add cheese, add bacon, no onions, extra pickles." The chef follows instructions step by step and gives you exactly what you asked for.
9.6. Code Examples
Python:
class HttpRequest:
def __init__(self):
self.method = "GET"
self.url = ""
self.headers = {}
self.body = None
class HttpRequestBuilder:
def __init__(self):
self._request = HttpRequest()
def method(self, method: str) -> 'HttpRequestBuilder':
self._request.method = method
return self
def url(self, url: str) -> 'HttpRequestBuilder':
self._request.url = url
return self
def header(self, key: str, value: str) -> 'HttpRequestBuilder':
self._request.headers[key] = value
return self
def json_body(self, data: dict) -> 'HttpRequestBuilder':
import json
self._request.body = json.dumps(data)
self.header("Content-Type", "application/json")
return self
def build(self) -> HttpRequest:
return self._request
# Usage
request = (HttpRequestBuilder()
.method("POST")
.url("https://api.example.com/users")
.header("Authorization", "Bearer token")
.json_body({"name": "Sankar"})
.build())
C++:
class HttpRequestBuilder {
private:
HttpRequest request;
public:
HttpRequestBuilder& setMethod(const std::string& method) {
request.method = method;
return *this;
}
HttpRequestBuilder& setUrl(const std::string& url) {
request.url = url;
return *this;
}
HttpRequestBuilder& addHeader(const std::string& key, const std::string& value) {
request.headers[key] = value;
return *this;
}
HttpRequest build() {
return request;
}
};
JavaScript:
class HttpRequestBuilder {
constructor() {
this.request = { method: "GET", url: "", headers: {}, body: null };
}
method(method) { this.request.method = method; return this; }
url(url) { this.request.url = url; return this; }
header(key, value) { this.request.headers[key] = value; return this; }
jsonBody(data) {
this.request.body = JSON.stringify(data);
return this.header("Content-Type", "application/json");
}
build() { return this.request; }
}
const request = new HttpRequestBuilder()
.method("POST")
.url("https://api.example.com")
.jsonBody({ name: "Sankar" })
.build();
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
10. 5. Prototype
Complexity: ★★☆☆☆ | Frequency: ★★★☆☆
10.1. Definition
Prototype creates new objects by cloning existing ones, rather than creating from scratch.
10.2. Why Is It Required?
When object creation is expensive (database calls, complex initialization), or when you need objects with slight variations from a template.
10.3. With vs Without
| Without Prototype | With Prototype |
|---|---|
| Recreate complex objects from scratch | Clone and modify — much faster |
| Expensive initialization repeated | Clone cached templates |
10.4. Best Use Cases
- Web Dev: Cloning form templates, cached API responses
- Game Dev: Spawning enemies from templates, particle systems
- Enterprise: Document templates, default settings
10.5. Analogy
Cell Division (Mitosis) — Cells don't build new cells from scratch. They duplicate themselves — DNA, organelles, everything copied, then split. The new cell is a perfect clone, ready to differentiate.
10.6. Code Examples
Python:
import copy
class GameCharacter:
def __init__(self, name, health, attack, skills):
self.name = name
self.health = health
self.attack = attack
self.skills = skills
def clone(self):
return copy.deepcopy(self)
# Prototype registry
prototypes = {
"warrior": GameCharacter("Warrior", 100, 15, ["Slash", "Block"]),
"mage": GameCharacter("Mage", 60, 25, ["Fireball", "Teleport"])
}
# Clone and customize
warrior1 = prototypes["warrior"].clone()
warrior1.name = "Conan"
warrior2 = prototypes["warrior"].clone()
warrior2.name = "Xena"
warrior2.skills.append("Leap Attack")
C++:
class GameCharacter {
public:
std::string name;
int health, attack;
std::vector<std::string> skills;
virtual std::unique_ptr<GameCharacter> clone() const = 0;
};
class Warrior : public GameCharacter {
public:
std::unique_ptr<GameCharacter> clone() const override {
return std::make_unique<Warrior>(*this);
}
};
JavaScript:
class GameCharacter {
constructor(name, health, attack, skills) {
this.name = name;
this.health = health;
this.attack = attack;
this.skills = skills;
}
clone() {
return new GameCharacter(
this.name,
this.health,
this.attack,
[...this.skills] // Deep copy array
);
}
}
const warrior = new GameCharacter("Warrior", 100, 15, ["Slash"]);
const warrior2 = warrior.clone();
warrior2.name = "Custom Warrior";
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
Structural Patterns
Mnemonic: "Adapters Bridge Composite Decorators, Facades Fly with Proxies"
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
Structural patterns deal with object composition — how classes and objects combine to form larger structures.
11. 6. Adapter
Complexity: ★★☆☆☆ | Frequency: ★★★★★
11.1. Definition
Adapter converts the interface of a class into another interface that clients expect.
11.2. Analogy
Power Plug Adapters — Your laptop charger doesn't fit foreign outlets. A plug adapter converts between socket types without rewiring anything.
11.3. Best Use Cases
- Web Dev: Integrating multiple payment gateways, normalizing API formats
- Game Dev: Supporting multiple input devices
- Enterprise: Legacy system integration
11.4. Code Example (Python)
class LegacyXmlService:
def parse_xml_document(self, xml_string):
return ET.fromstring(xml_string)
class XmlParserAdapter: # Adapts to expected interface
def __init__(self):
self._legacy = LegacyXmlService()
def parse(self, data: str) -> dict:
root = self._legacy.parse_xml_document(data)
return self._element_to_dict(root)
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
12. 7. Bridge
Complexity: ★★★★☆ | Frequency: ★★★☆☆
12.1. Definition
Bridge decouples an abstraction from its implementation so both can vary independently.
12.2. Analogy
Remote Control and TV — A remote (abstraction) works with any TV (implementation). Upgrade your TV without changing how you use the remote.
12.3. Best Use Cases
- Web Dev: UI components rendering on different platforms
- Game Dev: Game objects across different rendering engines
- Enterprise: Notification system (message types × delivery channels)
12.4. Code Example (Python)
class MessageSender(ABC): # Implementation
@abstractmethod
def send(self, title: str, body: str) -> None:
pass
class EmailSender(MessageSender):
def send(self, title, body):
print(f"📧 EMAIL | {title}: {body}")
class Notification(ABC): # Abstraction
def __init__(self, sender: MessageSender):
self._sender = sender
class AlertNotification(Notification):
def notify(self, message):
self._sender.send("🚨 ALERT", message)
# Mix and match!
AlertNotification(EmailSender()).notify("Server down!")
AlertNotification(SmsSender()).notify("Server down!")
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
13. 8. Composite
Complexity: ★★★☆☆ | Frequency: ★★★★☆
13.1. Definition
Composite lets you compose objects into tree structures to represent part-whole hierarchies, treating individuals and groups uniformly.
13.2. Analogy
Company Organization Chart — A CEO can ask for total salary of "Engineering Department." That department contains teams, each with people. Same getTotalSalary() works on individual, team, or entire department.
13.3. Best Use Cases
- Web Dev: DOM trees, nested menus, form builders
- Game Dev: Scene graphs, inventory systems
- Enterprise: Organizational hierarchies, bill-of-materials
13.4. Code Example (Python)
class FileSystemItem(ABC):
@abstractmethod
def get_size(self) -> int:
pass
class File(FileSystemItem):
def __init__(self, name, size):
self.name = name
self._size = size
def get_size(self):
return self._size
class Folder(FileSystemItem):
def __init__(self, name):
self.name = name
self._children = []
def add(self, item):
self._children.append(item)
def get_size(self):
return sum(child.get_size() for child in self._children)
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
14. 9. Decorator
Complexity: ★★★☆☆ | Frequency: ★★★★★
14.1. Definition
Decorator attaches additional responsibilities to an object dynamically, providing flexible alternative to subclassing.
14.2. Analogy
Coffee Orders at a Café — Start with espresso. Add milk (latte), add syrup, add whipped cream. Each addition "decorates" the base, adding cost and features.
14.3. Best Use Cases
- Web Dev: Middleware stacks, adding caching/logging/auth
- Game Dev: Power-ups, weapon modifications, buff/debuff systems
- Enterprise: Stream wrappers (encryption, compression)
14.4. Code Example (Python)
class DataSource(ABC):
@abstractmethod
def write(self, data: str) -> None:
pass
class FileDataSource(DataSource):
def write(self, data):
print(f"Writing: {data}")
class DataSourceDecorator(DataSource):
def __init__(self, source: DataSource):
self._wrapped = source
class EncryptionDecorator(DataSourceDecorator):
def write(self, data):
encrypted = self._encrypt(data)
self._wrapped.write(encrypted)
# Stack decorators!
decorated = LoggingDecorator(
CompressionDecorator(
EncryptionDecorator(FileDataSource())
)
)
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
15. 10. Facade
Complexity: ★★☆☆☆ | Frequency: ★★★★★
15.1. Definition
Facade provides a simplified interface to a complex subsystem.
15.2. Analogy
Hotel Concierge — A hotel has restaurants, spa, laundry, transportation. As a guest, you just talk to the concierge for everything. One call handles all your needs.
15.3. Best Use Cases
- Web Dev: SDK libraries, jQuery (DOM simplification)
- Game Dev: High-level engine APIs
- Enterprise: Service aggregation layers, API gateways
15.4. Code Example (Python)
class VideoConversionFacade:
def convert(self, filename: str, format: str) -> str:
"""Simple interface hiding complex subsystems"""
file = VideoFile(filename)
codec = CodecFactory.extract(file)
buffer = BitrateReader.read(filename, codec)
buffer = AudioMixer.fix(buffer)
return VideoConverter.convert(buffer, format)
# Usage - So simple!
VideoConversionFacade().convert("movie.avi", "mp4")
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
16. 11. Flyweight
Complexity: ★★★★☆ | Frequency: ★★☆☆☆
16.1. Definition
Flyweight minimizes memory by sharing common state among multiple objects.
16.2. Analogy
Typography and Fonts — A 100,000-character document doesn't store separate images for each letter. It stores which font to use (shared) and where each letter appears (unique).
16.3. Best Use Cases
- Web Dev: Character rendering in text editors, icon libraries
- Game Dev: Particle systems, terrain tiles, crowds
- Enterprise: String interning, connection pools
16.4. Code Example (Python)
class TreeFactory:
_tree_types = {}
@classmethod
def get_tree_type(cls, name, color, texture):
key = f"{name}_{color}"
if key not in cls._tree_types:
cls._tree_types[key] = TreeType(name, color, texture)
return cls._tree_types[key]
# 1000 trees share only 3 TreeType objects!
for i in range(1000):
tree_type = TreeFactory.get_tree_type("Oak", "green", "oak.png")
trees.append(Tree(x, y, tree_type))
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
17. 12. Proxy
Complexity: ★★★☆☆ | Frequency: ★★★★☆
17.1. Definition
Proxy provides a surrogate or placeholder for another object to control access.
17.2. Analogy
Credit Card as Proxy for Cash — A credit card is a proxy for money in your bank. Same "payment interface" but adds controls: spending limits, delayed withdrawal, transaction logging.
17.3. Best Use Cases
- Web Dev: Image lazy loading, API caching, authentication wrappers
- Game Dev: Asset loading proxies, network game objects
- Enterprise: Service proxies, access control wrappers
17.4. Code Example (Python)
class ImageProxy:
def __init__(self, filename):
self.filename = filename
self._real_image = None # Lazy
def display(self):
if self._real_image is None:
self._real_image = HighResolutionImage(self.filename)
self._real_image.display()
def get_info(self):
# Can answer without loading heavy image
return f"Proxy for {self.filename}"
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
Behavioral Patterns
Mnemonic: "Clever Code Inside Iterators Makes Memories Of State Strategies, Templates, & Visitors"
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
Behavioral patterns deal with communication between objects.
18. 13. Chain of Responsibility
Complexity: ★★★☆☆ | Frequency: ★★★☆☆
18.1. Definition
Chain of Responsibility passes requests along a chain of handlers. Each handler decides to process or pass it along.
18.2. Analogy
Customer Support Escalation — Level 1 handles basic issues. Can't help? Escalate to Level 2. Still unresolved? Level 3 specialist. Each level either handles or passes up.
18.3. Best Use Cases
- Web Dev: Middleware pipelines (Express, Django), authentication chains
- Game Dev: Input event handling, damage calculations
- Enterprise: Approval workflows, exception handling
18.4. Code Example (Python)
class SupportHandler(ABC):
def __init__(self):
self._next = None
def set_next(self, handler):
self._next = handler
return handler
@abstractmethod
def handle(self, request):
pass
def pass_to_next(self, request):
if self._next:
return self._next.handle(request)
return "No handler available"
class Level1Support(SupportHandler):
def handle(self, request):
if request.amount <= 100:
return "Level 1: Approved"
return self.pass_to_next(request)
# Build chain
level1.set_next(level2).set_next(manager)
level1.handle(request)
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
19. 14. Command
Complexity: ★★★☆☆ | Frequency: ★★★★★
19.1. Definition
Command encapsulates a request as an object, enabling parameterization, queuing, logging, and undoable operations.
19.2. Analogy
Restaurant Order System — Waiter writes order on ticket (command). Ticket goes to kitchen. Waiter doesn't cook; kitchen doesn't take orders. Tickets can be queued or cancelled.
19.3. Best Use Cases
- Web Dev: Undo/redo in editors, keyboard shortcuts, request queues
- Game Dev: Input replay, macro recording, turn-based actions
- Enterprise: Transaction systems, audit logging, job queues
19.4. Code Example (Python)
class Command(ABC):
@abstractmethod
def execute(self) -> None:
pass
@abstractmethod
def undo(self) -> None:
pass
class WriteCommand(Command):
def __init__(self, editor, text):
self.editor = editor
self.text = text
def execute(self):
self.editor.write(self.text)
def undo(self):
self.editor.delete(len(self.text))
class CommandManager:
def __init__(self):
self.history = []
def execute(self, command):
command.execute()
self.history.append(command)
def undo(self):
if self.history:
self.history.pop().undo()
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
20. 15. Interpreter
Complexity: ★★★★★ | Frequency: ★☆☆☆☆
20.1. Definition
Interpreter defines a grammatical representation for a language and provides an interpreter to evaluate sentences.
20.2. Analogy
Musical Notation — Sheet music is a language. Each symbol interpreted by musician: whole note = 4 beats, half note = 2 beats. Musician interprets according to music grammar.
20.3. Best Use Cases
- Web Dev: Template engines, query parsers
- Game Dev: Scripting languages, formula evaluators
- Enterprise: Business rule engines, SQL-like parsers
20.4. Code Example (Python)
class Expression(ABC):
@abstractmethod
def interpret(self, context: dict) -> int:
pass
class Number(Expression):
def __init__(self, value):
self.value = value
def interpret(self, context):
return self.value
class Variable(Expression):
def __init__(self, name):
self.name = name
def interpret(self, context):
return context[self.name]
class Add(Expression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) + self.right.interpret(context)
# (x + 5)
expr = Add(Variable("x"), Number(5))
print(expr.interpret({"x": 10})) # 15
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
21. 16. Iterator
Complexity: ★★☆☆☆ | Frequency: ★★★★★
21.1. Definition
Iterator provides way to access elements of a collection sequentially without exposing underlying representation.
21.2. Analogy
Spotify Playlist — When you hit "next track," you don't care if playlist is array, linked list, or from server. Just have "next," "previous," "current."
21.3. Best Use Cases
- Web Dev: Paginated data, lazy loading streams
- Game Dev: Entity iteration, pathfinding results
- Enterprise: Database cursors, message queue consumers
21.4. Code Example (Python)
class DepthFirstIterator:
def __init__(self, root):
self._stack = [root] if root else []
def has_next(self):
return len(self._stack) > 0
def next(self):
current = self._stack.pop()
for child in reversed(current.children):
self._stack.append(child)
return current
# Usage
dfs = DepthFirstIterator(tree.root)
while dfs.has_next():
print(dfs.next().value)
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
22. 17. Mediator
Complexity: ★★★☆☆ | Frequency: ★★★☆☆
22.1. Definition
Mediator defines object that encapsulates how objects interact, promoting loose coupling.
22.2. Analogy
Air Traffic Control Tower — Planes don't communicate directly. They talk to control tower (mediator), which coordinates everything. Tower knows all planes; planes only know tower.
22.3. Best Use Cases
- Web Dev: Form validation coordination, chat rooms
- Game Dev: NPC coordination, traffic systems
- Enterprise: Service orchestration, event aggregators
22.4. Code Example (Python)
class ChatRoom:
def __init__(self):
self.users = []
def add_user(self, user):
self.users.append(user)
def send_message(self, message, sender):
for user in self.users:
if user != sender:
user.receive(message, sender)
class User:
def __init__(self, name, room):
self.name = name
self.room = room
room.add_user(self)
def send(self, message):
self.room.send_message(message, self)
def receive(self, message, sender):
print(f"{self.name} received from {sender.name}: {message}")
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
23. 18. Memento
Complexity: ★★★☆☆ | Frequency: ★★★☆☆
23.1. Definition
Memento captures and externalizes object's internal state without violating encapsulation, for later restoration.
23.2. Analogy
Video Game Save Points — Save at checkpoints captures everything: health, inventory, position. Die? Load from save and restore exactly that state.
23.3. Best Use Cases
- Web Dev: Form drafts, state management snapshots
- Game Dev: Save systems, undo in level editors
- Enterprise: Transaction rollback, document versioning
23.4. Code Example (Python)
class EditorMemento:
def __init__(self, content, cursor):
self._content = content
self._cursor = cursor
def get_state(self):
return {"content": self._content, "cursor": self._cursor}
class TextEditor:
def __init__(self):
self.content = ""
self.cursor = 0
def save(self):
return EditorMemento(self.content, self.cursor)
def restore(self, memento):
state = memento.get_state()
self.content = state["content"]
self.cursor = state["cursor"]
class History:
def __init__(self, editor):
self.editor = editor
self.history = []
def backup(self):
self.history.append(self.editor.save())
def undo(self):
if self.history:
self.editor.restore(self.history.pop())
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
24. 19. Observer
Complexity: ★★☆☆☆ | Frequency: ★★★★★
24.1. Definition
Observer defines one-to-many dependency so when one object changes, all dependents are notified automatically.
24.2. Analogy
YouTube Subscriptions — Subscribe to channel, receive notifications on new videos. You don't poll; YouTube pushes to all subscribers. Unsubscribe anytime.
24.3. Best Use Cases
- Web Dev: Event listeners, reactive state (Redux, MobX), WebSocket
- Game Dev: Achievement systems, UI updates
- Enterprise: Stock tickers, notification systems
24.4. Code Example (Python)
class Stock:
def __init__(self, symbol, price):
self.symbol = symbol
self._price = price
self._observers = set()
@property
def price(self):
return self._price
@price.setter
def price(self, value):
self._price = value
self._notify()
def attach(self, observer):
self._observers.add(observer)
def _notify(self):
for obs in self._observers:
obs.update(self)
class PriceDisplay:
def update(self, stock):
print(f"Display: {stock.symbol} = ${stock.price}")
# Usage
aapl = Stock("AAPL", 150)
aapl.attach(PriceDisplay())
aapl.price = 155 # Display automatically notified
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
25. 20. State
Complexity: ★★★☆☆ | Frequency: ★★★★☆
25.1. Definition
State allows object to alter behavior when internal state changes. Object appears to change its class.
25.2. Analogy
Vending Machine — Behaves differently per state: Idle (waiting for money), HasMoney (waiting for selection), Dispensing (giving product). Same buttons, different results per state.
25.3. Best Use Cases
- Web Dev: Form wizards, checkout flows
- Game Dev: Character states (idle, running, jumping), enemy AI
- Enterprise: Order processing, approval workflows
25.4. Code Example (Python)
class OrderState(ABC):
@abstractmethod
def proceed(self, order):
pass
class PendingState(OrderState):
def proceed(self, order):
print("Payment confirmed!")
order.set_state(ProcessingState())
class ProcessingState(OrderState):
def proceed(self, order):
print("Order shipped!")
order.set_state(ShippedState())
class Order:
def __init__(self):
self._state = PendingState()
def set_state(self, state):
self._state = state
def proceed(self):
self._state.proceed(self)
order = Order()
order.proceed() # Payment confirmed!
order.proceed() # Order shipped!
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
26. 21. Strategy
Complexity: ★★☆☆☆ | Frequency: ★★★★★
26.1. Definition
Strategy defines family of algorithms, encapsulates each, makes them interchangeable.
26.2. Analogy
Google Maps Route Options — Choose: Fastest, Shortest, Avoid Highways, Scenic. Same destination, different strategies. Switch anytime.
26.3. Best Use Cases
- Web Dev: Payment methods, sorting options, auth strategies
- Game Dev: AI behaviors, movement patterns
- Enterprise: Tax calculation, shipping algorithms, discount rules
26.4. Code Example (Python)
class ShippingStrategy(ABC):
@abstractmethod
def calculate(self, items) -> float:
pass
class StandardShipping(ShippingStrategy):
def calculate(self, items):
return 5.0 + sum(i.weight for i in items) * 0.5
class ExpressShipping(ShippingStrategy):
def calculate(self, items):
return 15.0 + sum(i.weight for i in items) * 1.0
class ShoppingCart:
def __init__(self):
self.items = []
self._shipping = StandardShipping()
def set_shipping(self, strategy):
self._shipping = strategy
def get_total(self):
return sum(i.price for i in self.items) + self._shipping.calculate(self.items)
cart = ShoppingCart()
cart.set_shipping(ExpressShipping()) # Easy swap!
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
27. 22. Template Method
Complexity: ★★☆☆☆ | Frequency: ★★★★☆
27.1. Definition
Template Method defines skeleton of algorithm in base class, letting subclasses override specific steps.
27.2. Analogy
Building a House — Every house: Foundation → Frame → Roof → Exterior → Interior. Sequence fixed; materials/styles differ (brick vs wood, flat vs gable).
27.3. Best Use Cases
- Web Dev: HTTP lifecycle (parse → auth → handle → respond)
- Game Dev: Game loop (input → update → render)
- Enterprise: Report generation, ETL pipelines
27.4. Code Example (Python)
class DataMiner(ABC):
def mine(self, path):
"""Template method - skeleton"""
raw = self.extract(path)
parsed = self.parse(raw)
analyzed = self.analyze(parsed)
self.generate_report(analyzed)
@abstractmethod
def extract(self, path):
pass
@abstractmethod
def parse(self, data):
pass
def analyze(self, data): # Default implementation
return {"count": len(data)}
def generate_report(self, analysis): # Hook
print(f"Report: {analysis}")
class PDFMiner(DataMiner):
def extract(self, path):
return f"PDF content from {path}"
def parse(self, data):
return data.split()
class CSVMiner(DataMiner):
def extract(self, path):
return "a,b,c\n1,2,3"
def parse(self, data):
return [line.split(",") for line in data.split("\n")]
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
28. 23. Visitor
Complexity: ★★★★★ | Frequency: ★★☆☆☆
28.1. Definition
Visitor lets you define new operations on objects without changing their classes.
28.2. Analogy
Tax Auditor Visiting Departments — Auditor visits Finance, HR, Engineering. Each department has own structure, but auditor applies same audit process. Add new auditors without changing departments.
28.3. Best Use Cases
- Web Dev: AST transformations (Babel, ESLint), DOM traversal
- Game Dev: Collision handling, serialization
- Enterprise: Document exporters (PDF, HTML, XML)
28.4. Code Example (Python)
class DocumentVisitor(ABC):
@abstractmethod
def visit_heading(self, heading):
pass
@abstractmethod
def visit_paragraph(self, paragraph):
pass
class HTMLExporter(DocumentVisitor):
def visit_heading(self, h):
return f"<h{h.level}>{h.text}</h{h.level}>"
def visit_paragraph(self, p):
return f"<p>{p.text}</p>"
class MarkdownExporter(DocumentVisitor):
def visit_heading(self, h):
return f"{'#' * h.level} {h.text}"
def visit_paragraph(self, p):
return p.text
class Heading:
def __init__(self, level, text):
self.level = level
self.text = text
def accept(self, visitor):
return visitor.visit_heading(self)
# Export same document to different formats
doc.export(HTMLExporter())
doc.export(MarkdownExporter())
28.5.
| Analogy | UML Diagram | Minimalist Icon |
|---|---|---|
![]() | ![]() | ![]() |
Additional Modern Patterns
29. 24. Dependency Injection
Complexity: ★★★☆☆ | Frequency: ★★★★★
Objects receive dependencies from external sources rather than creating them internally. Enables loose coupling and easy testing.
# Without DI - tightly coupled
class UserService:
def __init__(self):
self.db = MySQLDatabase() # Hard-coded
# With DI - loosely coupled
class UserService:
def __init__(self, db: Database): # Injected
self.db = db
service = UserService(PostgresDatabase()) # Easy swap
30. 25. Repository
Complexity: ★★★☆☆ | Frequency: ★★★★★
Mediates between domain and data mapping layers, acting like in-memory collection.
class UserRepository:
def __init__(self, db_session):
self.session = db_session
def find_by_id(self, user_id):
return self.session.query(User).filter_by(id=user_id).first()
def save(self, user):
self.session.add(user)
self.session.commit()
31. 26. Unit of Work
Complexity: ★★★★☆ | Frequency: ★★★★☆
Maintains list of objects affected by business transaction and coordinates writing changes.
class UnitOfWork:
def __init__(self):
self.new = []
self.dirty = []
self.removed = []
def commit(self):
# Insert new, update dirty, delete removed
# All in single transaction
pass
Quick Reference & Mnemonics
32. Pattern Categories
| Category | Patterns | Focus |
|---|---|---|
| Creational | Singleton, Factory Method, Abstract Factory, Builder, Prototype | Object creation |
| Structural | Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy | Object composition |
| Behavioral | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor | Object communication |
33. When to Use What
| Problem | Pattern |
|---|---|
| Need exactly one instance | Singleton |
| Creating objects without specifying exact class | Factory Method |
| Creating families of related objects | Abstract Factory |
| Complex object construction | Builder |
| Cloning existing objects | Prototype |
| Making incompatible interfaces work together | Adapter |
| Separating abstraction from implementation | Bridge |
| Tree structures with uniform treatment | Composite |
| Adding behavior dynamically | Decorator |
| Simplifying complex subsystems | Facade |
| Sharing state to save memory | Flyweight |
| Controlling access to objects | Proxy |
| Passing requests along a chain | Chain of Responsibility |
| Encapsulating requests as objects | Command |
| Building language interpreters | Interpreter |
| Sequential access to collections | Iterator |
| Centralizing complex communication | Mediator |
| Capturing and restoring state | Memento |
| Notifying dependents of changes | Observer |
| Changing behavior based on state | State |
| Swapping algorithms at runtime | Strategy |
| Defining algorithm skeletons | Template Method |
| Adding operations without changing classes | Visitor |
34. Memory Mnemonics
34.1. Creational (5 patterns)
"Smart Factories Always Build Prototypes"
Singleton, Factory Method, Abstract Factory, Builder, Prototype
34.2. Structural (7 patterns)
"Adapters Bridge Composite Decorators, Facades Fly with Proxies"
Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
34.3. Behavioral (11 patterns)
"Clever Code Inside Iterators Makes Memories Of State Strategies, Templates, & Visitors"
Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
Design patterns are tools, not rules. Use them when they simplify your code, not to show off that you know them!




































































