A quick reference guide to all design patterns with use cases and relationships.
Purpose: Ensure a class has only one instance and provide global access.
When to Use:
- Need exactly one instance (logger, database connection, configuration)
- Controlled access to shared resource
- Lazy initialization needed
Example: Logger, Database Connection Pool, Configuration Manager
Implementation Concerns:
- Thread safety
- Serialization
- Reflection attacks
Purpose: Create objects without specifying exact classes. Subclasses decide which class to instantiate.
When to Use:
- Don't know exact types at compile time
- Want to delegate object creation to subclasses
- Need to add new types without modifying existing code
Example: Notification system (Email, SMS, Push), Document creators
vs Abstract Factory: Factory Method creates one product, Abstract Factory creates families of products.
Purpose: Provide interface for creating families of related objects without specifying concrete classes.
When to Use:
- System needs to support multiple platforms/families
- Products in a family should be used together
- Want to hide platform-specific code
Example: UI components (Windows, Mac, Linux), Database connections (MySQL, PostgreSQL)
Purpose: Construct complex objects step by step. Same construction process can create different representations.
When to Use:
- Object has many optional parameters
- Want to avoid telescoping constructor
- Need to construct object step by step
- Want immutable objects
Example: SQL Query Builder, HTTP Request Builder, Configuration Builder
Purpose: Create objects by cloning existing instances rather than creating new ones.
When to Use:
- Object creation is expensive
- Want to avoid subclassing
- Need to create objects at runtime
- System should be independent of how products are created
Example: Document cloning, Game object spawning, Template-based creation
Purpose: Allow incompatible interfaces to work together by wrapping one interface with another.
When to Use:
- Want to use existing class with incompatible interface
- Integrating third-party libraries
- Legacy code integration
Example: Payment gateway adapters, Media players, Logging adapters
vs Decorator: Adapter changes interface, Decorator adds behavior.
Purpose: Decouple abstraction from implementation so both can vary independently.
When to Use:
- Want to avoid permanent binding between abstraction and implementation
- Both abstraction and implementation should be extensible
- Changes in implementation shouldn't affect clients
Example: UI rendering (Web, Mobile), Device drivers, Database drivers
Purpose: Compose objects into tree structures. Clients treat individual objects and compositions uniformly.
When to Use:
- Represent part-whole hierarchies
- Want clients to ignore difference between individual and composite objects
- Need recursive operations
Example: File system, UI components, Organization hierarchies
Purpose: Attach additional responsibilities to objects dynamically. Flexible alternative to subclassing.
When to Use:
- Add responsibilities to objects at runtime
- Subclassing would be impractical (too many combinations)
- Want to add/remove features dynamically
Example: Java I/O streams, Middleware, Feature toggles
vs Adapter: Decorator adds behavior, Adapter changes interface.
Purpose: Provide simplified interface to complex subsystem.
When to Use:
- Want to simplify complex subsystem
- Need to decouple clients from subsystem
- Provide higher-level interface
Example: API Gateway, Library interfaces, Framework facades
Purpose: Use sharing to support large numbers of fine-grained objects efficiently.
When to Use:
- Application uses large number of objects
- Storage costs are high
- Most object state can be made extrinsic
- Groups of objects can be replaced with few shared objects
Example: Text editor (character objects), Game (tree/grass objects), Browser (DOM nodes)
Purpose: Provide placeholder for another object to control access.
When to Use:
- Need to control access to object
- Add functionality when accessing object (caching, logging, security)
- Lazy initialization
- Remote proxy (RPC)
Example: Caching proxy, Virtual proxy (lazy loading), Protection proxy, Remote proxy
Purpose: Define one-to-many dependency. When subject changes, all observers are notified.
When to Use:
- Change to one object requires changing others
- Number of dependent objects is unknown
- Loose coupling between objects
Example: Model-View architecture, Event handling, Stock price alerts
vs Pub-Sub: Observer is direct, Pub-Sub has message broker in between.
Purpose: Define family of algorithms, encapsulate each, make them interchangeable.
When to Use:
- Multiple ways to perform task
- Want to avoid conditional statements for algorithm selection
- Algorithms should be interchangeable
Example: Payment methods, Sorting algorithms, Compression algorithms, Validation rules
vs Factory: Strategy selects algorithm, Factory creates objects.
Purpose: Encapsulate request as object, allowing parameterization, queuing, logging, and undo.
When to Use:
- Need to parameterize objects with operations
- Need to queue operations
- Need to support undo/redo
- Need to log requests
Example: Text editor (undo/redo), Job queues, Macro recording, Transaction systems
Purpose: Allow object to alter behavior when internal state changes. Object appears to change its class.
When to Use:
- Object behavior depends on state
- Operations have large conditional statements based on state
- State transitions are well-defined
Example: Vending machine, ATM, Workflow engine, Game character states
Purpose: Pass request along chain of handlers. Each handler decides to process or pass to next.
When to Use:
- More than one object may handle request
- Don't know which handler should handle request
- Want to decouple sender and receiver
Example: Middleware, Exception handling, Event processing, Approval workflows
Purpose: Define algorithm skeleton, deferring some steps to subclasses.
When to Use:
- Common algorithm with varying steps
- Want to control algorithm structure
- Subclasses should redefine certain steps
Example: Data processing pipelines, Framework hooks, Build systems
Purpose: Define how set of objects interact. Promotes loose coupling.
When to Use:
- Set of objects communicate in complex ways
- Reusing objects is difficult because of tight coupling
- Behavior distributed across classes should be customizable
Example: Chat rooms, UI frameworks, Air traffic control
Purpose: Capture and externalize object's internal state without violating encapsulation.
When to Use:
- Need to save and restore object state
- Direct access to state would expose implementation
- Want to provide undo functionality
Example: Text editor (undo/redo), Game checkpoints, Configuration snapshots
Factory Method vs Abstract Factory:
- Factory Method: One product, one creator
- Abstract Factory: Family of products, one factory
Adapter vs Decorator:
- Adapter: Changes interface
- Decorator: Adds behavior, same interface
Strategy vs State:
- Strategy: Algorithm selection
- State: Behavior based on internal state
Command vs Strategy:
- Command: Encapsulates operation as object
- Strategy: Encapsulates algorithm
- What problem am I solving?
- What will change in the future?
- What relationships exist?
- Do I need runtime flexibility?
- What's the simplest solution?
- Using pattern when simple solution works
- Over-engineering
- Forcing pattern where it doesn't fit
- Not understanding pattern before using
Need single instance? → Singleton Creating objects? → Factory, Builder, Prototype Integrating incompatible interfaces? → Adapter Adding behavior dynamically? → Decorator Tree structure? → Composite One-to-many notifications? → Observer Multiple algorithms? → Strategy State-dependent behavior? → State Encapsulate operations? → Command Request handling chain? → Chain of Responsibility
- Creational Patterns Questions
- Structural Patterns Questions
- Behavioral Patterns Questions
- "Head First Design Patterns" book
- "Design Patterns: Elements of Reusable OO Software" (Gang of Four)
Remember: Patterns are tools, not goals. Use them when they solve real problems, not just because they exist!