GoF Design Patterns
Hello! This is Pan-kun.
This time I'll go back to basics and organize/explain the GoF (Gang of Four) design patterns. I have difficulty remembering proper nouns, so there may be omissions or slight variations in wording, but I will clearly convey the main ideas and practical caveats. I will list sources (URLs and quoted excerpts) at the end of the article as evidence for factual claims. When adopting these patterns in production, always consult primary sources (the original text or trusted references).
Introduction
"GoF" (Gang of Four) refers to the book published in 1994, "Design Patterns: Elements of Reusable Object-Oriented Software," which collected reusable object-oriented design solutions (patterns). The 23 patterns summarized by the GoF are generally classified into three categories:
- Creational: deal with object creation mechanisms
- Structural: deal with composition of classes and objects
- Behavioral: deal with communication between objects and defining algorithms
Patterns are not rigid "answers" but rather design recipes or templates. Advances in language features and frameworks can make some patterns unnecessary—or conversely, overusing patterns can introduce unnecessary complexity. Adopt patterns with judgement appropriate to the use case.
Evidence (excerpt):
- "A software design pattern describes a reusable solution to a commonly needed behavior in software." — Software design pattern (Wikipedia)
en.wikipedia.orgSoftware design pattern - Wikipedia
- "Design Patterns: Elements of Reusable Object-Oriented Software (1994) is a software engineering book describing software design patterns." — Design Patterns (Wikipedia)
en.wikipedia.orgDesign Patterns - Wikipedia
The citations above are intended as general references. When implementing or adopting a pattern, check the original works and concrete language-specific examples.
Brief explanations of each category
Creational
Patterns concerned with how objects are created. By separating construction logic, you can improve dependency management, testability, and extensibility.Structural
Patterns concerned with composing classes or objects into larger structures. They help with interface adaptation, separation of concerns, and reducing duplication.Behavioral
Patterns that focus on responsibilities, interactions between objects, and runtime algorithm variation. They aim to reduce coupling and improve testability.
1. Creational Patterns
Below are five representative creational patterns. For each I summarize the key idea, when to use it, and practical caveats.
Singleton
| Item | Description |
|---|---|
| Key idea | Ensure a class has only one instance and provide global access to it. |
| Pros | Centralizes access to shared resources. |
| Cons | Can make testing hard due to lifecycle/global state. Consider dependency injection as an alternative. |
Prototype
| Item | Description |
|---|---|
| Key idea | Create new objects by cloning existing instances. |
| Pros | Reduces initialization cost via copying; useful for dynamic object creation. |
| Cons | Be careful with deep vs. shallow copy and shared references. |
Use case: Cloning objects that have complex initialization.
Factory Method
| Item | Description |
|---|---|
| Key idea | Provide a method for subclasses to decide which concrete class to instantiate. |
| Pros | Makes it easy to extend how classes are created and lets clients depend on abstractions rather than concrete classes. |
| Cons | Class hierarchies can grow when many concrete types are needed. |
Abstract Factory
| Item | Description |
|---|---|
| Key idea | Provide an interface for creating families of related or dependent objects. |
| Pros | Produce compatible sets of objects consistently; implementation swapping is easy. |
| Cons | Adding new product types can require extensive changes across implementations. |
Builder
| Item | Description |
|---|---|
| Key idea | Separate the construction of a complex object from its representation so the same construction process can create different representations. |
| Pros | Enables readable construction code and staged configuration. |
| Cons | Builder implementations can become complex if the construction process is complicated. |
Use case: Domain models with many optional parameters or multi-stage initialization.
2. Structural Patterns
Below are representative structural patterns, presented in the same format as the creational patterns.
Adapter
| Item | Description |
|---|---|
| Key idea | Provide a wrapper that converts an existing class's interface into one expected by the client. |
| Pros | Allows connecting legacy code or external libraries without modifying them. |
| Cons | Too many adapters can reduce readability and increase maintenance cost. |
Use case: Adapting an old API to a new interface.
Bridge
| Item | Description |
|---|---|
| Key idea | Separate an abstraction from its implementation so the two can vary independently. |
| Pros | Makes it easy to switch or add implementations. |
| Cons | Can introduce structural complexity; may be overengineering if not planned early. |
Use case: Decoupling platform-dependent implementations (e.g., switching rendering backends).
Composite
| Item | Description |
|---|---|
| Key idea | Represent part-whole hierarchies so clients treat individual objects and compositions uniformly. |
| Pros | Enables recursion-friendly structures and simplifies client code. |
| Cons | Responsibilities between leaf and composite nodes can become unclear. |
Use case: Representing hierarchical UI components or file system structures.
Decorator
| Item | Description |
|---|---|
| Key idea | Dynamically add responsibilities to an object. More flexible than subclassing for composition of behavior. |
| Pros | Add behavior without modifying existing objects; combine decorators to build features. |
| Cons | A large number of decorators can make debugging and tracing behavior harder. |
Use case: Chaining stream processing (e.g., compression, encryption).
Facade
| Item | Description |
|---|---|
| Key idea | Provide a simplified, unified interface to a complex subsystem. |
| Pros | Simplifies client usage and hides internal implementation details. |
| Cons | Putting too much logic in the facade can bloat its responsibilities. |
Use case: Aggregating multiple APIs into a single service interface.
Flyweight
| Item | Description |
|---|---|
| Key idea | Extract shared intrinsic state from many similar objects so that it can be shared; clients supply extrinsic state. |
| Pros | Greatly reduces memory usage when many similar objects are needed. |
| Cons | Managing shared vs. external state can be tricky. |
Use case: Text editors sharing font and glyph information across many character objects.
Proxy
| Item | Description |
|---|---|
| Key idea | Provide a surrogate or placeholder for another object to control access, lazily initialize, or represent a remote object. |
| Pros | Control access transparently and save resources via lazy loading. |
| Cons | Adds overhead and can obscure the distinction between proxy and real object, complicating debugging. |
Use case: Lazy loading, remote stubs, or access control proxies.
Structural patterns increase indirection, so always weigh readability and runtime cost when applying them.
3. Behavioral Patterns
Below are representative behavioral patterns, summarized similarly.
Chain of Responsibility
| Item | Description |
|---|---|
| Key idea | Chain multiple handlers and pass a request along the chain until a handler processes it. |
| Pros | Reduces coupling between sender and receiver; makes adding or reordering handlers easy. |
| Cons | Harder to trace which handler processed a request during debugging; be careful about requests that go unhandled. |
Use case: Logging frameworks or request filter chains.
Command
| Item | Description |
|---|---|
| Key idea | Encapsulate an operation as an object. |
| Pros | Abstracts actions, making it easier to parameterize, queue, or undo operations. |
| Cons | Can add object overhead and complexity in simple cases. |
Use case: GUI actions with undo/redo support.
Interpreter
| Item | Description |
|---|---|
| Key idea | Represent a grammar as an object structure and provide an interpreter to evaluate sentences in that grammar. |
| Pros | Allows dynamic control of program behavior via external expressions. |
| Cons | Can be hard to implement and maintain; performance may be a concern. |
Use case: Expression evaluators, small scripting languages, or domain-specific string formats.
Iterator
| Item | Description |
|---|---|
| Key idea | Provide an abstraction for sequentially accessing elements of an aggregate without exposing its representation. |
| Pros | Offers a consistent way to traverse different collection implementations. |
| Cons | Requires careful design for concurrent modification during iteration. |
Use case: Collection traversal or stream processing.
Mediator
| Item | Description |
|---|---|
| Key idea | Centralize complex interactions between many objects in a mediator to reduce direct dependencies. |
| Pros | Simplifies individual classes and reduces coupling. |
| Cons | Mediator can become complex and a maintenance bottleneck if overloaded. |
Use case: Coordinating UI component interactions or message dispatch in a chat room.
Memento
| Item | Description |
|---|---|
| Key idea | Capture and externalize an object's internal state so it can be restored later without violating encapsulation. |
| Pros | Allows state snapshots while preserving encapsulation. |
| Cons | Copying/storing state can be costly; watch memory usage. |
Use case: Undo stacks in text editors.
Observer (Publish-Subscribe)
| Item | Description |
|---|---|
| Key idea | Notify multiple registered objects when another object's state changes. |
| Pros | Implements one-to-many notifications and achieves loose coupling. |
| Cons | Be mindful of event order, thread-safety, and potential memory leaks from forgotten deregistration. |
Use case: Event-driven UIs and real-time subscription models.
State
| Item | Description |
|---|---|
| Key idea | Delegate behavior to state objects based on an object's current state, making transitions explicit. |
| Pros | Organizes complex conditional logic by state; improves clarity. |
| Cons | Can produce many state classes; document sequences and relations for maintainability. |
Use case: Workflow engines or protocol state machines.
Strategy
| Item | Description |
|---|---|
| Key idea | Encapsulate a family of algorithms and make them interchangeable at runtime. |
| Pros | Enables independent testing and extension of algorithms; reduces conditional logic. |
| Cons | May be over-abstraction for simple scenarios. |
Use case: Swappable sorting algorithms or payment calculation strategies.
Template Method
| Item | Description |
|---|---|
| Key idea | Define the skeleton of an algorithm in a superclass, letting subclasses implement specific steps. |
| Pros | Reuse common behavior while allowing variations in subclasses. |
| Cons | Can increase coupling between subclasses and the superclass, reducing flexibility. |
Use case: Processing pipelines or sequences of operations with common steps.
Visitor
| Item | Description |
|---|---|
| Key idea | Add new operations to object structures without changing the structures themselves. |
| Pros | Makes adding new operations easy while keeping the object structure stable. |
| Cons | Requires extending the element interfaces; structural changes to elements force visitor updates. |
Use case: Traversal-based analyses on abstract syntax trees (e.g., type checking or optimization).
Practical caveats when adopting patterns
Think first whether the pattern is really needed
If the problem doesn't match the pattern, don't introduce it. Simpler implementations are often sufficient.Check whether language or framework features already provide an alternative
Functional and dynamic languages can often express GoF patterns more succinctly or render some patterns unnecessary.Prioritize testability and dependency management
Be cautious with patterns that introduce global state or tight coupling.Document your choices
Record why a pattern was adopted in README/design docs. This helps future refactors and reviews.Understand performance/maintenance trade-offs
Indirection gives flexibility but can increase call overhead and cognitive load.
Conclusion
GoF design patterns are powerful tools, but it's important to apply them appropriately rather than blindly. Understand each pattern's intent and consider language features and existing frameworks before adopting one.
Design depends on experience and context. Patterns expand your "design vocabulary" but are not an end in themselves. Start applying patterns in small, deliberate ways and share knowledge with your team.
(These days it's rare not to be familiar with these patterns, but still.)
Next time I'll explain detailed usage cases for several patterns with code examples.
Loading comments...