Design Patterns Guide
Design Patterns Guide
Design patterns are reusable solutions to commonly occurring problems in software design.
They represent best practices and capture experience in a way that others can reuse. This
guide covers the 23 original Gang of Four (GoF) patterns.
Key Concepts
• Von Neumann Architecture: Code and data are the same
• OOP Separation: Code (methods) fixed, Data (members) variable
• Lambdas: Anonymous functions that can capture outside variables
A. Creational Patterns
Patterns for creating objects without having to know all the details
1. Factory Method
Problem Need polymorphic object construction but languages like Java/C++ don't
have virtual constructors
Solution Create a virtual method in parent class that child classes override to return
specific object types
When to Use When you need to delegate object creation to subclasses while keeping
client code independent of specific classes
2. Abstract Factory
Problem Need to create families of related objects without specifying their concrete
classes
When to Use When system should work with multiple product families (Windows/Mac UI,
MySQL/PostgreSQL databases)
3. Builder
Problem Complex objects need step-by-step construction with many optional
parameters
When to Use When creating complex objects with many optional parameters (avoid
telescoping constructors)
4. Prototype
Problem Need to create objects by copying existing instances when creation is
expensive
Solution Clone existing objects instead of creating new ones. Objects implement
clone() method
5. Singleton
Problem Ensure only one instance of a class exists and provide global access to it
When to Use Use sparingly! Good for: logging, database connections, hardware
interface access. Bad for: hiding dependencies, making testing difficult
B. Structural Patterns
Patterns for assembling objects into larger structures that are flexible and efficient
1. Adapter
Problem Two interfaces don't match but need to work together
When to Use When integrating third-party libraries, legacy code, or APIs with
incompatible interfaces
2. Bridge
Problem Abstraction and implementation should vary independently (avoid cartesian
product explosion)
Example Example: Shape (Circle, Square) and Color (Red, Blue). Without Bridge:
RedCircle, BlueCircle, RedSquare, BlueSquare classes. With Bridge:
Shape has Color, only 4 classes total instead of multiplication
3. Composite
Problem Need to represent part-whole hierarchies of objects uniformly
Solution Create tree structure where leaf and composite nodes share same
interface
Example Example: FileSystem - File (leaf) and Directory (composite) both implement
FileSystemItem. Directory contains FileSystemItems. Can call getSize() on
file or directory recursively
When to Use When dealing with tree structures: UI components, organization charts, file
systems
4. Decorator
Problem Need to add responsibilities to objects dynamically without altering their
structure
Solution Wrap object with decorator classes that add new behavior while
maintaining same interface
When to Use When you want to add features to objects without inheritance explosion
(alternative to subclassing)
5. Facade
Problem Complex subsystem with many classes is difficult to use
When to Use When you need to simplify complex APIs or provide high-level interface to
complex subsystem
6. Flyweight
Problem Many similar objects consume too much memory due to duplicate data
Solution Share common data between objects, store unique data separately
When to Use When you have huge number of similar objects (particles in games,
characters in documents)
7. Proxy
Problem Need to control access to an object or defer its creation/initialization
Solution Create surrogate object that controls access to the real object
When to Use Virtual proxy (lazy loading), Protection proxy (access control), Remote
proxy (network calls), Cache proxy
C. Behavioral Patterns
Patterns for communication between objects and assignment of responsibilities
1. Chain of Responsibility
Problem Request needs to be processed by one of several handlers, but which one
isn't known in advance
Solution Chain handlers together. Each decides to process or pass to next handler
When to Use Event handling in UI, middleware in web servers, approval workflows
2. Command
Problem Need to decouple sender from receiver, queue operations, support undo
3. Iterator
Problem Need to traverse collection without exposing internal structure
Solution Provide interface with hasNext() and next() methods to traverse elements
When to Use Built into modern languages (for-in loops). Custom traversal orders,
multiple simultaneous iterations
4. Mediator
Problem Many objects need to communicate, creating complex dependencies
Example Example: ChatRoom mediator. Users don't send messages directly to each
other. They send to ChatRoom, which distributes to relevant users. Users
only know ChatRoom, not each other
When to Use GUI components interaction, air traffic control, chat applications
5. Memento
Problem Need to save/restore object state without violating encapsulation
6. Observer
Problem Multiple objects need notification when another object changes state
7. State
Problem Object behavior changes based on internal state (avoiding huge switch
statements)
Solution Create state classes for each state. Object delegates to current state
object
When to Use State machines, game character AI, UI components with modes
8. Strategy
Problem Multiple algorithms exist for a task, need to select at runtime
9. Template Method
Problem Algorithm structure is same but some steps vary between implementations
When to Use Framework hooks, data processing pipelines, game AI turn sequences
10. Visitor
Problem Need to add operations to class hierarchy without modifying classes
Solution Operations become visitor objects that visit elements of hierarchy