The Gang of Four book files its twenty-three patterns under Creational / Structural / Behavioral. That taxonomy is useful for looking something up and terrible for learning, for one reason: the patterns that are easiest to confuse sit in different chapters. Strategy and Bridge draw the same class diagram. So do Strategy and State. Learn them weeks apart and “so what’s the difference?” never gets resolved.
So this series is ordered by which patterns look alike, and each module ends by naming what separates them. Because what distinguishes two patterns is never the structure – it is the intent.
Code is Kotlin. Every pattern is tagged with what the language did to it: replaced (a language feature does the job), reshaped (there is an idiomatic Kotlin form worth knowing), or as-is (nothing in the language helps – write it the way GoF describes).
Three principles underneath all of it
- Program to an interface, not an implementation.
- Favor object composition over class inheritance.
- Encapsulate what varies.
Memorizing twenty-three patterns is not the goal. Two sentences from the GoF preface and one that follows from them are effectively the whole thing, and the patterns are variations on those three. For each new pattern, answer in one line: which of the three is this doing, and how?
The map
M0FoundationsSession 1
| Foundations | What to look at when you look at a pattern – and how to tell when not to reach for one at all. | |
M1Swapping AlgorithmsSession 2-3
| Strategy | Encapsulate a family of algorithms as objects and swap them at runtime; the client picks. | replaced |
| Template Method | Fix the skeleton of an algorithm and let subclasses supply the varying steps. | replaced |
| State | Let an object change its behavior wholesale as its internal state changes; the object owns the transitions. | reshaped |
M2Separating Object CreationSession 4-5
| Factory Method | Defer the choice of which concrete class to instantiate to a subclass. | reshaped |
| Abstract Factory | Create whole families of related objects that must be used together, without naming their concrete classes. | as-is |
| Builder | Separate the construction of a complex object from its representation. | replacedoutline |
| Prototype | Create new objects by cloning an existing instance. | replacedoutline |
| Singleton | Force a class to have exactly one instance and give it a global access point. | replacedoutline |
M3Wrappers: The Four SiblingsSession 6-7
| Adapter | Change an interface so two otherwise incompatible things can work together. | replacedoutline |
| Decorator | Keep the interface and add responsibilities, stackable at runtime. | replacedoutline |
| Proxy | Keep the interface and control access – lazy loading, caching, permissions, remoting. | replacedoutline |
| Facade | Put one simple interface in front of a complicated subsystem. | as-isoutline |
M4Trees and RecursionSession 8-9
| Composite | Treat individual objects and compositions of objects uniformly. | reshapedoutline |
| Iterator | Walk the elements of a collection without exposing how it is stored. | replacedoutline |
| Visitor | Add a new operation over an object structure without modifying the structure. | reshapedoutline |
| Interpreter | Represent a grammar as a class hierarchy and evaluate sentences written in it. | reshapedoutline |
M5Communication Between ObjectsSession 10-11
| Observer | Notify a set of subscribers automatically when state changes; the publisher does not know who is listening. | reshapedoutline |
| Mediator | Replace direct references between objects with a hub that coordinates them. | as-isoutline |
| Chain of Responsibility | Pass a request along a chain until some handler takes it, and can stop it. | reshapedoutline |
| Command | Turn a request into an object so it can be queued, logged, and undone. | reshapedoutline |
M6State, Resources, Two-Axis GrowthSession 12
| Memento | Capture and restore an object’s state without breaking encapsulation. | replacedoutline |
| Flyweight | Share common intrinsic state across many objects to cut memory. | as-isoutline |
| Bridge | Split abstraction from implementation so the two can vary independently. | as-isoutline |
Before adopting any of them
Three gates, all of which must pass:
- Has the change actually arrived twice? No predicting. Once is a coincidence; twice is a pattern.
- Is there exactly one clear axis of variation? Two means consider Bridge. Unclear means it is too early.
- Does the code get shorter, or at least easier to follow, afterward? If there are now three more files and the flow is harder to trace, that is a failure. “It’s the correct design” is not a justification.
See also: Confusing Pairs – the patterns whose structure is identical and whose intent is not.