Here’s a concise yet practical guide to the 23 GoF (Gang of Four) design patterns, grouped by category, with:
✅ When to use
🏛️
Architectural use case examples
🔷 1. Creational Patterns
Concerned with object creation mechanisms, hiding the
logic and making the system independent of how objects are created.
Pattern |
When to Use |
Architecture Use Case |
Singleton |
When you need one and only one instance of a class |
Configuration, Logging, Connection Pool |
Factory Method |
When you need to create objects without exposing
instantiation logic |
Plugin loader, Task Scheduler |
Abstract Factory |
When you need to create families of related objects |
UI Toolkits (e.g., Dark/Light Theme components) |
Builder |
When you need to build a complex object step by step |
Building DTOs, Configuring Kafka consumers |
Prototype |
When object creation is costly and you want to clone
instead |
Cloning a configured workflow template or entity state |
🔷 2. Structural Patterns
Concerned with object composition, ensuring that
classes and objects work together to form larger structures.
Pattern |
When to Use |
Architecture Use Case |
Adapter |
When interfaces are incompatible but need to work together |
Integrating legacy system with new API |
Bridge |
When you want to separate abstraction from implementation |
Decouple UI control and rendering logic |
Composite |
When you want to treat individual objects and compositions
uniformly |
Tree structure like a file system or menu |
Decorator |
When you want to add responsibilities to objects
dynamically |
Logging, metrics around existing service methods |
Facade |
When you want to provide a simplified interface to
a complex subsystem |
Simplified APIs over a complex service layer |
Flyweight |
When you have lots of objects with shared state to
optimize memory |
Reusing icons, font objects in UI rendering |
Proxy |
When you need a placeholder to control access to another
object |
Lazy loading, security checks, or caching |
🔷 3. Behavioral Patterns
Concerned with communication between objects.
Pattern |
When to Use |
Architecture Use Case |
Chain of Responsibility |
When you want multiple objects to handle a request in a
chain |
Request validation chain, Middleware pipelines |
Command |
When you need to encapsulate requests as objects |
Undo/Redo, Task queues |
Interpreter |
When you need to evaluate sentences in a language |
Expression parsers, configuration DSL |
Iterator |
To access elements of a collection sequentially without
exposing underlying structure |
Database cursor, File reader |
Mediator |
When you want to centralize complex communication between
objects |
Chat rooms, Event buses |
Memento |
To restore object state to a previous state |
Undo operations, version control |
Observer |
When multiple objects need to be notified of state changes |
Event listeners, messaging subscribers |
State |
When an object needs to change behavior when its state
changes |
Stateful workflows, TCP state machine |
Strategy |
When you want to define a family of algorithms,
encapsulate each one |
Encryption strategies, Sorting algorithms |
Template Method |
When you want to define the skeleton of an algorithm,
deferring steps to subclasses |
Report generation with customizable steps |
Visitor |
When you want to perform operations on elements of object
structure without changing the classes |
Code generation, XML processing |
📘 Quick Mapping:
Architecture Use Cases by Pattern
Area |
Useful Patterns |
RESTful APIs |
Facade, Decorator, Chain of Responsibility |
Microservices |
Proxy, Mediator, Observer, Strategy |
UI Components |
Composite, Flyweight, Adapter, Bridge |
Cloud Infrastructure |
Singleton (for global config), Builder (resource
templates) |
Message Queues/Event-Driven |
Observer, Command, Mediator |
Security |
Proxy, Chain of Responsibility, Strategy (authN/authZ) |
ETL Pipelines |
Chain of Responsibility, Strategy, Template Method |
Monitoring/Logging |
Decorator, Observer |
Workflow Engine |
State, Command, Memento, Interpreter |
✅ Creational Patterns
🔹 Singleton
Answer:
I chose Singleton because we needed to ensure a
single global instance of a class like a configuration manager or logger.
It fits well in the architecture as it helps us centralize shared state
and avoid redundant initialization, especially in cases like managing
DB connection pools or global config.
It also keeps the design clean and efficient, avoiding unnecessary
object creation.
🔹 Factory Method
Answer:
I chose Factory Method because we needed to create
objects based on runtime input without exposing the instantiation logic.
It fits well in the architecture as it helps us encapsulate object creation
and adhere to the Open/Closed Principle, especially in scenarios like instantiating
different notification types (Email, SMS, Push).
It also keeps the design extensible and loosely coupled.
🔹 Builder
Answer:
I chose Builder because we needed to construct
complex objects with optional fields and immutable design.
It fits well in the architecture as it helps us build configurations or DTOs
in a readable and safe manner, especially in API request/response object
construction or job definitions.
It also keeps the design clean and fluent.
✅ Structural Patterns
🔹 Adapter
Answer:
I chose Adapter because we needed to integrate a
legacy system with a new interface without changing the old code.
It fits well in the architecture as it helps us bridge incompatible APIs,
especially when wrapping a SOAP service with a REST interface.
It also keeps the design flexible and backward-compatible.
🔹 Decorator
Answer:
I chose Decorator because we needed to add
additional behavior like logging or validation without modifying core logic.
It fits well in the architecture as it helps us layer responsibilities
dynamically, especially in Spring AOP or request interceptors.
It also keeps the design modular and open for extension.
🔹 Facade
Answer:
I chose Facade because we needed to simplify
access to a complex subsystem for clients.
It fits well in the architecture as it helps us expose a clean API over
multiple microservices or modules, especially in API Gateway or
Orchestrator services.
It also keeps the design easy to use and maintain.
✅ Behavioral Patterns
🔹 Chain of Responsibility
Answer:
I chose Chain of Responsibility because we needed to process
a request through a customizable sequence of handlers.
It fits well in the architecture as it helps us build a middleware-style
processing pipeline, especially in authentication → validation →
execution chains in request filters.
It also keeps the design modular and flexible.
🔹 Strategy
Answer:
I chose Strategy because we needed to swap
algorithms or business rules at runtime without changing the context.
It fits well in the architecture as it helps us plug in different pricing
models or filtering rules, especially in e-commerce engines or data
parsers.
It also keeps the design clean, testable, and open for new strategies.
🔹 Observer
Answer:
I chose Observer because we needed to notify
multiple systems when an event occurs.
It fits well in the architecture as it helps us decouple event emitters from
subscribers, especially in event-driven systems like Kafka consumers or
audit logs.
It also keeps the design scalable and reactive.
🔹 State
Answer:
I chose State because we needed to handle behavior
that changes based on internal state.
It fits well in the architecture as it helps us model state transitions in
workflows or finite state machines, especially in order processing or
protocol handling.
It also keeps the design structured and avoids complex conditionals.
No comments:
Post a Comment