Skip to main content

design-patterns-gof

The 23 Gang of Four object-oriented design patterns (Gamma, Helm, Johnson, Vlissides, 1994) distilled as a practical field guide, not a catalog. Use when naming a shape in code review, choosing between competing designs, deciding whether to introduce indirection, or auditing for over-abstraction. Each pattern documents intent, tradeoffs, modern relevance, language-level replacements, and when NOT to use it. Triggers on: "design pattern", "GoF", "factory", "builder", "singleton", "adapter", "decorator", "observer", "strategy", "visitor", "state machine", "command pattern", "too many constructors", "subclass explosion", "program to an interface", "composition over inheritance", "am I over-abstracting this", "Swift design patterns", "protocol-oriented programming", "POP in Swift", "Crusty talk", "protocol vs class", "value types over reference types", "start with a protocol".

Jump to install

Source facts

Repository
caidanw/skills
Last source activity
April 21, 2026 at 22:45
Detected SKILL.md language
English
Stars
8
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
27 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
design-patterns-gof
description
The 23 Gang of Four object-oriented design patterns (Gamma, Helm, Johnson, Vlissides, 1994) distilled as a practical field guide, not a catalog. Use when naming a shape in code review, choosing between competing designs, deciding whether to introduce indirection, or auditing for over-abstraction. Each pattern documents intent, tradeoffs, modern relevance, language-level replacements, and when NOT to use it. Triggers on: "design pattern", "GoF", "factory", "builder", "singleton", "adapter", "decorator", "observer", "strategy", "visitor", "state machine", "command pattern", "too many constructors", "subclass explosion", "program to an interface", "composition over inheritance", "am I over-abstracting this", "Swift design patterns", "protocol-oriented programming", "POP in Swift", "Crusty talk", "protocol vs class", "value types over reference types", "start with a protocol".
# Design Patterns (Gang of Four) Field guide to the 23 patterns from *Design Patterns: Elements of Reusable Object-Oriented Software* (Gamma, Helm, Johnson, Vlissides, 1994), updated for modern TypeScript/Python/Rust/Swift practice. Each pattern lives in its own reference file — this SKILL.md is the index and the meta-level (principles, categories, decision tree, criticism). ## Why use this skill - You want the **vocabulary** — naming a shape ("this is a Decorator chain") beats a paragraph of prose in PR review. - You're about to introduce an abstraction and want to sanity-check whether it earns its keep. - You're reading a codebase that leans on patterns and need a map. - You're translating a C++/Java pattern into a language with first-class functions and sum types. This skill is **opinionated**. In 2026, a lot of GoF ceremony dissolves into language features (closures, discriminated unions, generators, pattern matching, DI containers). The patterns that survived kept their names because the *shape* is useful; the *class hierarchies* often aren't. ## The two core principles (the spine of the book) Every pattern in the book is a recipe for one or both of these: 1. **Program to an interface, not an implementation.** Depend on the abstract contract, not the concrete class. You can swap implementations, test with fakes, and change internals without touching callers. 2. **Favor object composition over class inheritance.** Inheritance is white-box reuse — the subclass sees the parent's internals and is tightly coupled. Composition is black-box reuse — you hold a reference and call its interface. Most behavioral patterns exist to express composition in a language that defaulted to inheritance. When a pattern feels wrong, it's usually because one of these principles was violated (e.g., Template Method forces inheritance; Singleton exposes global state and hides dependencies). ## The four elements of a pattern (GoF Ch. 1) Every pattern documented here has: 1. **Name** — handle for design conversation. The vocabulary is the real product. 2. **Problem** — the context and preconditions that make the pattern applicable. 3. **Solution** — the participants, relationships, and collaborations. A template, not code. 4. **Consequences** — tradeoffs. **Most important and most often stripped** from tutorials. A pattern without stated consequences is a cargo cult. ## The 23 patterns at a glance ### Creational (5) — *object construction* | Pattern | One-line intent | Modern relevance | |---|---|---| | [Abstract Factory](references/creational/abstract-factory.md) | Produce families of related objects through one interface | Low-mid. DI containers subsume most uses. | | [Builder](references/creational/builder.md) | Construct complex objects step-by-step; avoid telescoping constructors | High (fluent APIs everywhere). Rust-canonical. | | [Factory Method](references/creational/factory-method.md) | Subclass (or function) decides which concrete class to instantiate | High. `Array.of`, `Promise.resolve`, `URL.createObjectURL`. | | [Prototype](references/creational/prototype.md) | Clone an existing instance instead of constructing from scratch | Low in app code; high in game engines / scene graphs. | | [Singleton](references/creational/singleton.md) | One instance, global access | **Mostly anti-pattern.** Use DI or module-level values. | ### Structural (7) — *object composition* | Pattern | One-line intent | Modern relevance | |---|---|---| | [Adapter](references/structural/adapter.md) | Translate one interface to another so incompatible objects can collaborate | Ubiquitous at system boundaries. | | [Bridge](references/structural/bridge.md) | Split abstraction and implementation into two hierarchies linked by composition | Moderate. JDBC, React renderers. | | [Composite](references/structural/composite.md) | Treat individual objects and groups uniformly via a tree | Ubiquitous. DOM, ASTs, scene graphs. | | [Decorator](references/structural/decorator.md) | Stack behaviors onto an object at runtime | Ubiquitous (middleware under different names). | | [Facade](references/structural/facade.md) | One narrow API over a complex subsystem | Very high. SDK clients, service classes. | | [Flyweight](references/structural/flyweight.md) | Share fine-grained objects to save memory | Niche. Graphics, text rendering, ECS. | | [Proxy](references/structural/proxy.md) | Surrogate that controls access to another object | Enormous. ORMs, gRPC stubs, Vue/MobX reactivity. | ### Behavioral (11) — *communication and responsibility* | Pattern | One-line intent | Modern relevance | |---|---|---| | [Chain of Responsibility](references/behavioral/chain-of-responsibility.md) | Pass a request through a chain of handlers | Ubiquitous as middleware. | | [Command](references/behavioral/command.md) | Encapsulate a request as a first-class object | High (CQRS, Redux actions, job queues). | | [Interpreter](references/behavioral/interpreter.md) | Represent a grammar and evaluate sentences in it | Low. Use parser combinators / ADTs. | | [Iterator](references/behavioral/iterator.md) | Traverse a collection without exposing its structure | Built into every modern language. | | [Mediator](references/behavioral/mediator.md) | Central hub coordinates peers so they don't talk directly | Moderate. Redux/Zustand stores, chat servers. | | [Memento](references/behavioral/memento.md) | Capture and restore an object's state without breaking encapsulation | High (undo, time travel, transactions). | | [Observer](references/behavioral/observer.md) | Publisher notifies many subscribers of state changes | Ubiquitous (EventEmitter, RxJS, signals). | | [State](references/behavioral/state.md) | Object's behavior changes with internal state via delegation | High. XState, typestate, UI state machines. | | [Strategy](references/behavioral/strategy.md) | Interchangeable algorithms behind one interface | Ubiquitous — usually just a function. | | [Template Method](references/behavioral/template-method.md) | Fixed algorithm skeleton; subclasses override steps | Mostly framework internals. Prefer Strategy. | | [Visitor](references/behavioral/visitor.md) | Add operations to an object structure without modifying it | Niche (compilers). Prefer pattern matching. | ## Decision tree — which category to look in ``` Is the pain about... ├─ CONSTRUCTION? → Creational │ • Too many constructor args / optional config? → Builder │ • Need to swap families together? → Abstract Factory │ • Subclass decides concrete type? → Factory Method │ • Construction expensive / copy cheaper? → Prototype │ • (Singleton: almost never — use DI.) │ ├─ SHAPE / WIRING? → Structural │ • Incompatible interfaces? → Adapter │ • Add responsibility without subclassing? → Decorator │ • Tree / part-whole with uniform treatment? → Composite │ • Simplify access to subsystem? → Facade │ • Control access / lazy / remote? → Proxy │ • Decouple two hierarchies (what vs. how)? → Bridge │ • Share millions of fine-grained objects? → Flyweight │ └─ BEHAVIOR / COMMS? → Behavioral • Swap algorithm at runtime? → Strategy (usually just a function) • Broadcast to many listeners? → Observer • Encapsulate request (queue / undo / log)? → Command (+ Memento for undo) • Step through a collection uniformly? → Iterator (usually built-in) • Walk a heterogeneous tree? → Visitor (prefer pattern matching) • Object acts differently per internal mode? → State • Skeleton with customizable steps? → Template Method (prefer Strategy) • Many-to-many chatter → one hub? → Mediator • Try handlers until one succeeds? → Chain of Responsibility • Capture/restore state? → Memento • Evaluate a domain grammar? → Interpreter (prefer parser combinators) ``` Before committing to any leaf, run **two checks**: 1. **Does my language already have this?** Strategy = function. Iterator = `for`/generator. Command = closure + data. Observer = pub/sub primitive. Visitor = `match` on union. If the language answers, stop. 2. **Have I seen this shape three times?** (Rule of Three, Fowler.) If not, inline it. Two occurrences aren't enough signal — the "common" shape is usually an illusion, and the wrong abstraction is more expensive than duplication (Sandi Metz). ## How patterns combine (the real value) Real systems stack 3–5 patterns. Key relationships from GoF's inter-pattern graph: - **Abstract Factory** is often built with **Factory Methods** or **Prototypes**. - **Composite** is almost always traversed with **Iterator** or **Visitor**. - **Decorator** and **Composite** share recursive structure; differ in intent (augment vs. aggregate). - **Command + Memento** = undo stack. - **Chain of Responsibility** is often built on **Composite** (the tree is the chain). - **Mediator** often uses **Observer** internally. - **State** and **Strategy** share a class diagram; differ in who drives transitions. - **Visitor** + **Composite** + **Iterator** is the compiler-writer's trio. - **Interpreter**'s AST is a **Composite**. ## When to reach for a pattern — and when you're over-abstracting **Signs you're over-abstracting:** - One concrete implementation behind a factory/interface. Delete the indirection. - Class names like `AbstractSingletonProxyFactoryBean` — the pattern chain is the identity. - "We'll need it later." You won't. YAGNI. - The pattern appears before the duplication (UML before the second caller). - Anemic domain: all behavior lives in services, entities are getter-bags. No pattern fixes a modeling problem. **Signs you actually need it:** - A shape has repeated three times with real variation. - Multiple *actual* implementations exist today (payment providers, renderers, formats). - You want a specific **consequence** (undo, part-whole uniformity, runtime algorithm swap). - The team needs a shared name to stop re-deriving the design in every review. ## Language-specific guides - **Swift** — [references/swift-idioms.md](references/swift-idioms.md) maps every GoF pattern to its Swift 5.10 / Swift 6 / iOS 17+ replacement (enum with associated values, `@Observable`, `@resultBuilder`, actors, property wrappers, `Codable`, `AsyncSequence`, `@dynamicMemberLookup`, structured concurrency, copy-on-write). Answers "is this pattern still idiomatic in Swift?" per pattern. - **Swift — Protocol-Oriented Programming** — [references/swift-protocol-oriented.md](references/swift-protocol-oriented.md) distills Dave Abrahams' WWDC 2015 "Crusty" talk (Session 408) and a decade of community retrospective. Covers why value types + protocols + extensions dissolve Template Method, Strategy, Abstract Factory, Bridge, Adapter, Visitor, Iterator — and the POP failure modes (protocol soup, PAT friction, `some`/`any` confusion) that led Apple to bring classes back via `@Observable` / `@Model`. Pair with `swift-idioms.md`: that file answers "what's the Swift replacement?"; this file answers "why does it work, where does it fail, when do I still use a class?" ## Criticism and modernity — essential context Three long-running critiques have become conventional wisdom; treat them as load-bearing: 1. **Patterns as workarounds for weak languages** (Paul Graham, "Revenge of the Nerds"). If you're hand-expanding a template, your language is missing a construct. 2. **Patterns dissolve in expressive languages** (Peter Norvig, 1996). 16 of 23 GoF patterns are invisible or trivial in Lisp/Dylan. In 2026 most of those language features exist in TS/Python/Rust/Swift/Kotlin — so the *class hierarchies* dissolve while the *shape names* stay useful. 3. **Cargo cult risk** (Jeff Atwood). Patterns as templates-to-apply rather than tools-for-problems produce classes whose names leak implementation rather than naming domain concepts. Full treatment in [references/criticism-and-modernity.md](references/criticism-and-modernity.md). ## What's still canonical vs. what aged poorly **Still alive under their GoF names:** Adapter, Facade, Proxy, Decorator, Observer, Composite, Iterator (as protocol), Strategy (as function), Builder, Factory Method, State, Command, Chain of Responsibility, Memento. **Mostly anti-patterns or heavily displaced:** - **Singleton** — global state, test-hostile. Replaced by DI or module-level values. - **Visitor** — replaced by pattern matching on sealed hierarchies / discriminated unions. - **Template Method** — violates composition-over-inheritance. Prefer Strategy. - **Abstract Factory** — absorbed by DI containers. - **Interpreter** — parser combinators and ADT + recursive `eval` are better. - **Prototype** — niche outside game engines; immutable data makes it redundant. ## How to use this skill 1. Identify the axis of pain (construction / structure / behavior) using the decision tree. 2. Read the candidate pattern's reference file. Look at **Modern Relevance** and **When NOT to Use** *before* you look at the structure — those sections tell you whether to even continue. 3. If you adopt the pattern, prefer the language-native form (function, union, closure) to the class-heavy GoF form unless you actually need the ceremony (serialization, persistence, cross- language interop, explicit dispatch). 4. Name it in code and PRs. The vocabulary is half the point.
View on GitHub