
Design patterns give us names for recurring design decisions: who creates an object, how components fit together, and who decides what happens next. Knowing a name is useful only when you can explain the problem it solves and the cost of adding it.
This is the starting point for my 24-post series: this overview and one article for each of the 23 patterns. The articles are based on my 23 Design Patterns repository, with complete Python and C++20 examples, expected output for each, and practical limitations. Each article pairs a familiar cover illustration with a sketch map of its example’s roles and flow.
What is a design pattern?
A pattern describes a recurring problem, the responsibilities that can solve it, and the consequences of arranging those responsibilities in a particular way. It is a design vocabulary, not a package you install or a requirement to create a fixed number of classes.
For example, choosing a shipping calculation can be a Strategy implemented as a callable. You do not need a deep inheritance hierarchy to express that choice. A Composite, on the other hand, becomes useful when a file and a folder need the same operation even though one folder owns more entries.
The familiar group of 23 comes from Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, often called the Gang of Four or GoF. The publisher’s catalog identifies the book and its pattern chapters. The explanations and runnable examples in this series come from my own repository.
What you need before starting
You should be comfortable reading a small program with functions, classes, constructors, and a few collaborating objects. You do not need to memorize the catalog first.
- Interfaces and contracts: know what an operation promises to its caller, including errors and lifetime requirements.
- Encapsulation: understand why a caller should not directly edit another object’s internal state.
- Composition and delegation: recognize when an object owns or borrows a collaborator and asks it to do part of the work.
- Inheritance and polymorphism: understand an overridable operation and a call made through a base interface.
- Ownership and lifetime: in the C++ examples, distinguish a value, a borrowed reference,
std::unique_ptr,std::shared_ptr, andstd::weak_ptr. - Testing: be able to predict output, try a boundary case, and explain what a small example does not test.
The repository’s glossary explains the terms used in each article. You can read the prose without installing anything. Run the first example with Python 3.10 or newer; the C++20 version needs a C++20 compiler, and the repository’s combined build needs CMake 3.20 or newer.
Three categories: what part of the design changes?
The category in each article title answers a practical question. It is the pattern’s purpose, not the name of a programming-language class.
| Category | Question | Typical pressure |
|---|---|---|
| Creational patterns | How should objects be created or configured? | Concrete construction details are spreading through callers. |
| Structural patterns | How should objects and classes fit together? | Interfaces, wrappers, or part-whole structures need a clearer boundary. |
| Behavioral patterns | How should work and communication be divided? | Algorithms, requests, transitions, or notifications are becoming tangled. |
There are five creational, seven structural, and eleven behavioral patterns in this series. A real design can combine patterns from more than one category.
Class scope and object scope
Purpose and scope are different ways of describing a pattern. Class scope emphasizes inheritance relationships and behavior supplied by subclasses. Object scope emphasizes collaborating instances, usually connected through composition or delegation.
Factory Method and Template Method make their extension points especially clear through inheritance. Strategy and Bridge illustrate composition: a context or abstraction keeps a collaborator behind a contract. Adapter has both class-based and object-based forms; the Adapter article uses an object adapter that borrows a sensor.
This distinction helps you ask where change happens. Must someone introduce a subclass, supply a different object, or do both? It does not mean object-scope patterns avoid classes. Their implementations still have types; the flexibility comes from how their objects are connected.
Creational patterns
In the Abstract Factory example, those pieces become a button and a panel from the same theme. Other creational patterns address different construction pressures.
Abstract Factory
Abstract Factory (Creational Pattern) — Create compatible families of objects without coupling the client to concrete classes.
Several related objects must vary together. The article explains the participating classes, the implementation, and when that extra structure is unnecessary.
Builder
Builder (Creational Pattern) — Construct a complex object step by step while allowing different final representations.
Construction has meaningful ordered steps. The article explains the participating classes, the implementation, and when that extra structure is unnecessary.
Factory Method
Factory Method (Creational Pattern) — Let subclasses or specialized creators decide which concrete product to instantiate.
A framework supplies the workflow but extensions choose products. The article explains the participating classes, the implementation, and when that extra structure is unnecessary.
Prototype
Prototype (Creational Pattern) — Create new objects by copying an existing configured prototype.
Initialization is expensive or discovered at runtime. The article explains the participating classes, the implementation, and when that extra structure is unnecessary.
Singleton
Singleton (Creational Pattern) — Provide one controlled instance and a global access point—while understanding the coupling it creates.
Exactly one process-wide coordinator is a real invariant. The article explains the participating classes, the implementation, and when that extra structure is unnecessary.
Structural patterns
Structural patterns organize connections. A wrapper may translate an interface, add behavior, or control access; those similar shapes have different intentions. Start by naming the boundary you need.
Adapter
Adapter (Structural Pattern) — Translate one interface into another interface the client already understands.
Integrating legacy or third-party code. Read the implementation and its trade-offs before choosing this structure.
Bridge
Bridge (Structural Pattern) — Separate an abstraction from its implementation so both dimensions can vary independently.
Two independent dimensions would otherwise multiply subclasses. Read the implementation and its trade-offs before choosing this structure.
Composite
Composite (Structural Pattern) — Treat individual objects and nested groups through the same component interface.
Data naturally forms a part-whole tree. Read the implementation and its trade-offs before choosing this structure.
Decorator
Decorator (Structural Pattern) — Add responsibilities by wrapping an object with components that share its interface.
Responsibilities must be combined dynamically. Read the implementation and its trade-offs before choosing this structure.
Facade
Facade (Structural Pattern) — Offer a focused entry point to a complex subsystem without removing lower-level access.
Most callers need a small set of common workflows. Read the implementation and its trade-offs before choosing this structure.
Flyweight
Flyweight (Structural Pattern) — Share repeated intrinsic state while keeping context-specific state outside the shared object.
Object count is huge and repeated state dominates memory. Read the implementation and its trade-offs before choosing this structure.
Proxy
Proxy (Structural Pattern) — Stand in for another object to control access, loading, location, or instrumentation.
Lazy loading, remote access, authorization, or caching is transparent to clients. Read the implementation and its trade-offs before choosing this structure.
Behavioral patterns
Behavioral patterns make responsibility explicit: who chooses an algorithm, handles an event, traverses data, coordinates peers, or remembers an earlier state. The important distinction is the collaboration, not the number of classes.
Chain of Responsibility
Chain of Responsibility (Behavioral Pattern) — Pass a request through an ordered chain until a handler processes it or the chain ends.
The receiver is selected dynamically from an ordered pipeline. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Command
Command (Behavioral Pattern) — Represent a request as an object so it can be queued, logged, retried, or undone.
Operations need queuing, history, retry, or undo. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Interpreter
Interpreter (Behavioral Pattern) — Represent a small grammar as objects that evaluate expressions in a context.
The grammar is small, stable, and evaluated often. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Iterator
Iterator (Behavioral Pattern) — Traverse a collection without exposing how that collection stores its elements.
A collection needs multiple traversal strategies. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Mediator
Mediator (Behavioral Pattern) — Centralize complex collaboration so peer objects do not depend directly on one another.
Peer-to-peer dependencies have become tangled. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Memento
Memento (Behavioral Pattern) — Capture and restore an object’s state without exposing its internal representation.
State snapshots are needed for undo or rollback. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Observer
Observer (Behavioral Pattern) — Notify a changing set of subscribers when a subject emits an event or changes state.
One event has multiple independently changing reactions. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
State
State (Behavioral Pattern) — Change an object’s behavior when its internal state changes by delegating to state objects.
Behavior changes substantially across explicit states. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Strategy
Strategy (Behavioral Pattern) — Package interchangeable algorithms behind one interface and choose among them by composition.
Several algorithms solve the same defined task. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Template Method
Template Method (Behavioral Pattern) — Define an algorithm’s stable skeleton while allowing selected steps to vary in subclasses.
The workflow order is invariant but selected steps vary. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Visitor
Visitor (Behavioral Pattern) — Add operations across a stable set of element types without placing every operation on those elements.
Element types are stable while new operations are frequent. Follow the article’s example, then try its challenge to explore a boundary the demonstration does not cover.
Where would I use these ideas?
The repository examples are small enough to trace. Their application contexts suggest where to investigate a pattern, not proof that the demonstrations are production-ready services.
| Situation | Patterns to compare | Question to settle first |
|---|---|---|
| Configuring a request or choosing a service implementation | Builder, Factory Method, Abstract Factory | Are you assembling options, choosing one product, or coordinating a family? |
| Connecting an existing library or simplifying a workflow | Adapter, Facade | Do you need compatibility or a smaller entry point? |
| Adding optional features or deferring expensive work | Decorator, Proxy | Are you adding responsibility or controlling access? |
| Choosing a shipping rule or managing a device lifecycle | Strategy, State | Is behavior selected as a policy or determined by the current state? |
| Editing a document with history | Command, Memento | Must you remember an action, a snapshot, or both? |
| Updating screens after a change | Observer, Mediator | Are you broadcasting a change or coordinating specific peers? |
| Processing a tree | Composite, Iterator, Visitor | Are you modeling the structure, traversing it, or adding typed operations? |
For instance, wrapping a payment library can make its interface easier to use, but it does not make a checkout transaction atomic. The Facade article explicitly keeps that limitation visible. Likewise, a Singleton may have safe initialization while its later mutations still need synchronization.
A practical reading order
If you are new to the subject, follow the repository’s progression rather than trying to memorize the alphabetical catalog:
- Strategy, Observer, Factory Method, and Adapter introduce small, visible boundaries.
- Decorator, Command, Composite, and State add composition, history, trees, and transitions.
- Facade, Builder, Template Method, Bridge, Proxy, Chain of Responsibility, and Mediator broaden the design choices.
- Abstract Factory, Prototype, Memento, Iterator, Flyweight, Interpreter, Visitor, and Singleton deserve extra attention to ownership, copying, dispatch, and global state.
For each article, read the problem before the code. Predict the output, trace who owns each object, then change the example using its mini challenge. Compare the simplest alternative with the pattern and explain which change made the extra structure worthwhile.
Run the C++20 examples
Clone the repository and run these commands there, not in the portfolio source:
git clone https://github.com/sanfor2004/23-Design-Patterns.git
cd 23-Design-Patterns
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug
ctest --test-dir build -C Debug --output-on-failure
On Windows, use a Visual Studio developer shell with C++ tools installed. The build guide explains single-pattern targets and compiler choices. Each example has a main.cpp and an expected.txt; the checks compare the demonstration’s output, not every possible behavior of an application using the pattern.
When you do not need a pattern
Keep the simple design when one function, a small value type, or a readable conditional expresses the requirement. Repetition in syntax is not always evidence of the same responsibility. An abstraction chosen before the variation is understood can make unrelated changes depend on one another.
Before introducing a pattern, answer four questions: What is changing? Which caller currently knows too much? What contract will remain stable? What new cost will the pattern introduce? Costs include extra types, indirection, copying, retained memory, difficult lifetimes, and less visible control flow.
The aim of this series is to make those decisions explainable. Use the complete illustrated index above to find a pattern, and return here when two patterns seem similar.
Source and illustration notes
The 23 Design Patterns repository is the source for the C++20 examples and adapted articles. It also contains comparisons, a cheat sheet, and a relationship map.
The overview’s dedicated cover opens a sketchbook onto three connected ideas: a workshop creates components, modular bridges combine them into structures, and signal stations exchange information. It introduces the three pattern families through a visual analogy, not a code diagram.
All 23 individual pattern covers are preserved from the site’s earlier learning collection. They illustrate analogies; the separate diagrams show the example’s structure. The overview cover was generated separately for this introduction; illustration provenance records its creation. Source code, adapted documentation, and diagrams retain the repository’s license notice.