Skip to content

Latest commit

 

History

History
335 lines (227 loc) · 9.26 KB

File metadata and controls

335 lines (227 loc) · 9.26 KB

Design Patterns Overview

A quick reference guide to all design patterns with use cases and relationships.

📚 Creational Patterns

Singleton

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

Factory Method

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.


Abstract Factory

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)


Builder

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


Prototype

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


📚 Structural Patterns

Adapter

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.


Bridge

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


Composite

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


Decorator

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.


Facade

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


Flyweight

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)


Proxy

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


📚 Behavioral Patterns

Observer

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.


Strategy

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.


Command

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


State

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


Chain of Responsibility

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


Template Method

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


Mediator

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


Memento

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


🔗 Pattern Relationships

Similar Patterns

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

📝 Choosing the Right Pattern

Questions to Ask:

  1. What problem am I solving?
  2. What will change in the future?
  3. What relationships exist?
  4. Do I need runtime flexibility?
  5. What's the simplest solution?

Anti-Patterns:

  • Using pattern when simple solution works
  • Over-engineering
  • Forcing pattern where it doesn't fit
  • Not understanding pattern before using

🎯 Pattern Selection Guide

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


📚 Resources


Remember: Patterns are tools, not goals. Use them when they solve real problems, not just because they exist!