Chapter 03 · Article 08 of 55
Introduction to Design Patterns
Design patterns are reusable solutions to commonly occurring problems in software design. They are not finished code you can plug directly into your application - they are templ…
Article outline13 sections on this page+
Overview
Design patterns are reusable solutions to commonly occurring problems in software design. They are not finished code you can plug directly into your application - they are templates, blueprints, and proven approaches that guide you toward solving recurring architectural challenges.
Why do design patterns exist?
Software engineers kept solving the same structural problems repeatedly - managing object creation, composing classes into larger structures, and defining communication between objects. Patterns emerged as a shared vocabulary that allows teams to communicate complex ideas succinctly. Instead of explaining an entire object-creation strategy, you say "use a Factory" and every experienced developer understands the intent.
The concept of patterns originated in architecture. Christopher Alexander introduced the idea in A Pattern Language (1977), describing recurring solutions to problems in building design. Software engineers adopted this philosophy in the early 1990s, culminating in the landmark 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by the Gang of Four (GoF).
The Gang of Four (GoF)
The term "Gang of Four" refers to four authors who formalized 23 design patterns:
| Author | Contribution |
|---|---|
| Erich Gamma | Led pattern cataloguing; later created JUnit and contributed to Eclipse IDE |
| Richard Helm | Focused on structural patterns and pattern documentation format |
| Ralph Johnson | Brought academic rigour; research in frameworks and reflection |
| John Vlissides | Contributed to pattern relationships and consequences analysis |
Their 1994 book catalogued patterns observed in production systems (primarily C++ and Smalltalk). It did not invent these patterns - it named, documented, and systematised them. The book remains the definitive reference, though modern languages have rendered some patterns unnecessary (e.g., Iterator is built into most standard libraries).
Key insight: GoF patterns target object-oriented design. They assume inheritance, polymorphism, and encapsulation as foundational mechanisms.
Pattern Categories
The 23 GoF patterns divide into three families:
| Category | Purpose | Examples | When to Use |
|---|---|---|---|
| Creational | Control object creation mechanisms | Singleton, Factory Method, Abstract Factory, Builder, Prototype | When direct instantiation creates coupling, complexity, or inflexibility |
| Structural | Compose classes/objects into larger structures | Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy | When you need to simplify interfaces, add behaviour, or unify incompatible systems |
| Behavioural | Define communication and responsibility between objects | Observer, Strategy, Command, Chain of Responsibility, State, Template Method, Visitor, Mediator, Memento, Iterator | When algorithms vary, objects need loose coupling, or responsibilities must be distributed |
Rule of thumb: Creational patterns abstract what gets created. Structural patterns abstract how things are composed. Behavioural patterns abstract how things communicate.
Anatomy of a Pattern
Every GoF pattern is documented using a consistent structure:
| Section | Description |
|---|---|
| Intent | A one-sentence summary of what the pattern does |
| Motivation | A scenario illustrating the problem and how the pattern solves it |
| Applicability | Conditions under which the pattern is appropriate |
| Structure | UML class/sequence diagrams showing participants |
| Participants | Classes/objects involved and their roles |
| Collaborations | How participants interact at runtime |
| Consequences | Trade-offs - what you gain and what you pay |
When studying or presenting a pattern, always address Intent first (what problem does it solve?) and Consequences last (what are the trade-offs?). Interviewers value trade-off awareness over rote definitions.
When to Use Design Patterns
| Situation | Recommended Pattern(s) |
|---|---|
| Need exactly one instance globally | Singleton |
| Object creation logic is complex with many parameters | Builder |
| Must switch between algorithm families at runtime | Strategy |
| Want to decouple event producers from consumers | Observer |
| Need to support undo/redo operations | Command + Memento |
| Wrapping a legacy system with a clean interface | Adapter or Facade |
| Adding responsibilities dynamically without subclassing | Decorator |
| Creating families of related objects without specifying concrete classes | Abstract Factory |
| Need to traverse a collection without exposing internals | Iterator |
| Object creation should be deferred to subclasses | Factory Method |
| Cloning expensive-to-create objects | Prototype |
Decision framework: If you find yourself writing conditional logic to decide which class to instantiate or which algorithm to run, a pattern likely applies.
When NOT to Use Design Patterns
Design patterns are tools, not goals. Misapplication causes more harm than the problems they solve.
1. Over-engineering (YAGNI violation) Applying Abstract Factory when you have one concrete implementation and no foreseeable variation. You add indirection without benefit.
2. Pattern Fever Forcing every class into a pattern. Not every object needs a Factory. Not every callback needs an Observer. If the straightforward approach is readable and maintainable, prefer it.
3. KISS Violation A Singleton with double-checked locking in a language that provides module-level constants is unnecessary complexity. Modern languages often provide idiomatic alternatives that eliminate the need for classical patterns.
4. Wrong Abstraction Level Using Visitor in a codebase with 2 node types that will never grow. The pattern's complexity outweighs its benefit when the problem space is small and stable.
5. Cargo Culting Copying a pattern from a tutorial without understanding the problem it solves in your context. Patterns are context-dependent - the same pattern can be brilliant or disastrous depending on constraints.
Guideline: Apply a pattern when you feel the pain of not having it - not preemptively.
Pattern Relationships
Patterns do not exist in isolation. They complement, compete with, and build upon each other:
┌─────────────────────────────────────────┐
│ CREATIONAL │
│ │
│ Abstract Factory ──uses──► Factory Method│
│ │ │ │
│ │ creates │ creates │
│ ▼ ▼ │
│ Prototype Singleton │
│ │ │ │
│ └───► Builder ◄──────┘ │
└──────────────────┬──────────────────────-┘
│
objects flow into
▼
┌─────────────────────────────────────────┐
│ STRUCTURAL │
│ │
│ Adapter ◄──competes──► Bridge │
│ │ │ │
│ ▼ ▼ │
│ Facade ──simplifies──► Composite │
│ │ │ │
│ └──► Decorator ◄───────┘ │
│ │ Proxy │
└──────────────┼──────────────────────────-┘
│
communicates via
▼
┌─────────────────────────────────────────┐
│ BEHAVIOURAL │
│ │
│ Observer ◄──notifies──► Mediator │
│ │ │ │
│ ▼ ▼ │
│ Command ──────► Chain of Responsibility │
│ │ │ │
│ ▼ ▼ │
│ Strategy ◄──swaps──► State │
│ │ │
│ └──► Template Method ──► Iterator │
└─────────────────────────────────────────-┘
Key relationships:
- Abstract Factory often uses Factory Method internally
- Builder can use Prototype for complex part construction
- Decorator and Strategy both add behaviour - Decorator wraps, Strategy delegates
- State and Strategy share identical structure but differ in intent
Creational Patterns Overview
Creational patterns abstract the instantiation process, making systems independent of how objects are created, composed, and represented.
| Pattern | One-liner | When to Use |
|---|---|---|
| Singleton | Ensures a class has exactly one instance with a global access point | Shared resources (config, connection pool, logger) |
| Factory Method | Defines an interface for creating objects, letting subclasses decide which class to instantiate | When a class cannot anticipate the type of objects it must create |
| Abstract Factory | Creates families of related objects without specifying concrete classes | When the system must be independent of how products are created and composed |
| Builder | Separates construction of a complex object from its representation | When construction involves many steps or optional parameters |
| Prototype | Creates new objects by cloning an existing instance | When instantiation is expensive and objects differ only slightly |
Creational patterns shift responsibility: Instead of the client knowing exactly which class to instantiate, the pattern encapsulates that decision. This is the foundation of the Dependency Inversion Principle - depend on abstractions, not concretions.
Common Misconceptions
| Myth | Reality |
|---|---|
| "Design patterns are only for Java/C++" | Patterns are language-agnostic concepts; implementation varies by language |
| "Singleton is an anti-pattern" | Singleton is problematic when misused (global mutable state); it's valid for genuinely shared resources |
| "Using patterns makes code better automatically" | Misapplied patterns increase complexity; they must solve a real problem |
| "Patterns are outdated" | Core problems persist; some patterns are built into languages, but understanding why remains essential |
| "You should use as many patterns as possible" | Fewer patterns applied correctly beats many patterns applied poorly |
| "Patterns replace architecture" | Patterns are micro-level solutions; architecture addresses system-level concerns |
| "Factory and Abstract Factory are the same" | Factory Method uses inheritance; Abstract Factory uses composition to create families |
Interview Follow-ups
Q1: What is the difference between a design pattern and an algorithm?
A: An algorithm is a step-by-step procedure to solve a computational problem (sorting, searching). A design pattern is a structural template for organising code to solve a design problem (flexibility, decoupling, reuse). Algorithms solve "how to compute X"; patterns solve "how to structure code so X is maintainable."
Q2: Can you use multiple design patterns together? Give an example.
A: Absolutely. A common combination: Abstract Factory (creates UI components) + Singleton (ensures one factory instance) + Observer (UI components notify listeners on state change). MVC itself combines Observer (View observes Model), Strategy (Controller is a strategy for the View), and Composite (Views can be nested).
Q3: How do design patterns relate to SOLID principles?
A: Patterns are implementations of SOLID principles. Factory Method applies the Open/Closed Principle (extend via new subclasses without modifying existing code). Strategy applies Dependency Inversion (depend on an abstraction, not a concrete algorithm). Observer applies the Single Responsibility Principle (separates event production from consumption).
Q4: Why might you choose Prototype over Factory Method?
Hint: Think about when object creation is expensive and the new object differs only slightly from an existing one. Consider deep vs shallow copy implications.
Q5: In what scenario would you combine Builder with Singleton?
Hint: Consider configuration objects that are complex to construct but should exist only once. Think about thread-safe lazy initialisation of a multi-step build.
Counter Questions to Ask Interviewer
Use these to demonstrate depth and clarify requirements:
-
"What's the expected rate of change in this system?" - Determines whether patterns for extensibility (Strategy, Factory) are warranted or over-engineering.
-
"Are there existing patterns or conventions in the codebase I should follow?" - Shows awareness that consistency matters more than theoretical perfection.
-
"What are the concurrency requirements?" - Singleton, Observer, and Builder all have thread-safety implications that change implementation significantly.
-
"Is this a library/framework or an application?" - Libraries benefit heavily from Template Method and Strategy; applications may prefer simpler direct approaches.
-
"What's the team's familiarity with these patterns?" - A pattern the team doesn't understand creates maintenance burden regardless of technical merit.
References & Whitepapers
| Resource | Description |
|---|---|
| Design Patterns: Elements of Reusable Object-Oriented Software (Gamma et al., 1994) | The definitive GoF reference; 23 patterns with C++/Smalltalk examples |
| Head First Design Patterns (Freeman & Robson, 2004) | Accessible introduction with Java examples and visual learning approach |
| A Pattern Language (Christopher Alexander, 1977) | The architectural origin of pattern thinking; 253 patterns for buildings and towns |
| Pattern-Oriented Software Architecture (Buschmann et al., 1996) | Extends GoF to architectural patterns (Layers, Pipes & Filters, Broker) |
| Refactoring to Patterns (Kerievsky, 2004) | Bridges refactoring and patterns; shows when and how to introduce patterns incrementally |
Related Topics
- Factory Method Pattern
- Abstract Factory Pattern
- Builder Pattern
- Prototype Pattern
- Singleton Pattern
- SOLID Principles
- Structural Design Patterns
- Behavioural Design Patterns
This article serves as the gateway to the creational design patterns series. Each subsequent article dives deep into one pattern with real-world examples, code implementations, trade-off analysis, and interview-ready explanations.