Skip to content

Latest commit

 

History

History
393 lines (248 loc) · 26.9 KB

File metadata and controls

393 lines (248 loc) · 26.9 KB

Design Patterns by Problem

Generated by scripts/build_catalog.py. Do not edit by hand.

Every pattern below is used by at least one problem in this repository, in code you can run. Each entry says what the pattern is for, the question in a problem that makes you reach for it, and the mistake it tends to attract — then lists where to go and read one.

20 patterns across 17 problems.

At a glance

Pattern Problems
Abstract Factory Design a Payment Gateway
Adapter Design a Payment Gateway
Builder Design a Movie Ticket Booking System
Chain of Responsibility Design a Logging Framework, Design an ATM
Command Design a Parking Lot, Design a Text Editor with Undo, Design Chess
Composite Design a Text Editor with Undo, Design an In-Memory File System
Decorator Design a Notification Service
Facade Design a Chat Room, Design a Movie Ticket Booking System, Design a Parking Lot, Design a Payment Gateway, Design a Text Editor with Undo, Design an In-Memory File System
Factory Method Design a Notification Service, Design a Parking Lot
Iterator Design an In-Memory File System
Mediator Design a Chat Room
Memento Design a Text Editor with Undo
Observer Design a Chat Room, Design a Notification Service, Design an Elevator System, Design Chess, Design Splitwise, Design Tic Tac Toe
Proxy Design a Movie Ticket Booking System
Repository Design a Movie Ticket Booking System
Singleton Design a Logging Framework
State Design a Vending Machine, Design an ATM, Design an Elevator System
Strategy Design a Logging Framework, Design a Movie Ticket Booking System, Design a Notification Service, Design a Parking Lot, Design a Payment Gateway, Design a Rate Limiter, Design an Elevator System, Design an LRU Cache, Design Chess, Design Splitwise, Design Tic Tac Toe, Snake and Ladder
Template Method Design an LRU Cache, Design Chess
Visitor Design an In-Memory File System

Abstract Factory

Create families of related objects whose members have to be consistent with each other.

Reach for it when: Do these objects only make sense together — and would mixing two families be a bug rather than a style choice?

The usual mistake: Reaching for it when one product would do. If the members do not have to agree, a Factory Method is the smaller answer.

Worked examples:

  • Design a Payment Gateway (🔴 Hard) — alongside Adapter, Strategy, Facade
    Wrap three payment SDKs that disagree about units, method names and how failure is signalled behind one interface, then use an abstract factory so a region's processor, tax rules and money formatting can never be mixed up.

Adapter

Make an interface you do not control fit one you do.

Reach for it when: Am I wrapping something I cannot change — a vendor SDK, a legacy class, a protocol?

The usual mistake: Letting the foreign interface leak through anyway — a vendor exception type escaping, or an amount crossing the boundary in the wrong units.

Worked examples:

  • Design a Payment Gateway (🔴 Hard) — alongside Abstract Factory, Strategy, Facade
    Wrap three payment SDKs that disagree about units, method names and how failure is signalled behind one interface, then use an abstract factory so a region's processor, tax rules and money formatting can never be mixed up.

Builder

Assemble an object across several steps, validating only once it is complete.

Reach for it when: Does this object need many pieces, several optional, arriving at different moments?

The usual mistake: Using it for three arguments. A constructor is fine until half-built objects start escaping in an invalid state.

Worked examples:

  • Design a Movie Ticket Booking System (🔴 Hard) — alongside Repository, Proxy, Strategy, Facade
    Stop two people buying the same seat by holding it under an expiring lock taken before payment and released after, with a builder assembling the booking, a repository hiding where shows live, and a caching proxy in front of it.

Chain of Responsibility

Pass a request along a chain of handlers until one of them deals with it.

Reach for it when: Could any of several handlers deal with this, and should the sender not have to know which?

The usual mistake: Copying the pattern without asking whether the chain should stop. For logging it must not; for an approval workflow it must.

Worked examples:

  • Design a Logging Framework (🟡 Medium) — alongside Strategy, Singleton
    Route log records down a handler chain where every link applies its own threshold and format, so one chain can send everything to the console and only errors to a JSON file — and where the chain deliberately does not stop at the first handler that takes it.
  • Design an ATM (🟡 Medium) — alongside State
    Model an ATM as a state machine where each state permits only what is safe from it, and build the note dispenser as a chain of denomination handlers that plans the full amount before a single note leaves the box.

Command

Turn an operation into an object, so it can be queued, logged, or undone.

Reach for it when: Do I need undo, a replayable history, or a way to defer this action?

The usual mistake: Storing too little to invert the operation, or capturing that data at construction rather than at execution.

Worked examples:

  • Design a Parking Lot (🟡 Medium) — alongside Strategy, Factory Method, Facade
    Assign spots to arriving vehicles by a pluggable allocation strategy, charge for the stay on exit by a separate pricing strategy, and drive the whole thing from a command console where adding an operation never edits an existing one.
  • Design a Text Editor with Undo (🟡 Medium) — alongside Memento, Composite, Facade
    Build an editor whose every edit is a reversible command, where consecutive keystrokes coalesce into one undo step, replace-all undoes as a single action, and a new edit after an undo correctly discards the redo stack.
  • Design Chess (🔴 Hard) — alongside Strategy, Template Method, Observer
    Give each piece responsibility for how it moves, make every move an undoable command, and decide legality by playing a move and asking whether your own king is now attacked — verified against known perft counts to depth four.

Composite

Treat a single object and a group of objects through the same interface.

Reach for it when: Is this a tree where a container should be usable wherever a leaf is?

The usual mistake: Two typed child lists instead of one. Every traversal then gets written twice, and the copies drift.

Worked examples:

  • Design a Text Editor with Undo (🟡 Medium) — alongside Command, Memento, Facade
    Build an editor whose every edit is a reversible command, where consecutive keystrokes coalesce into one undo step, replace-all undoes as a single action, and a new edit after an undo correctly discards the redo stack.
  • Design an In-Memory File System (🟡 Medium) — alongside Visitor, Iterator, Facade
    Model files and directories as one composite tree so no caller ever branches on which it is holding, then add search, disk usage and rendering as visitors that walk the tree without living on it.

Decorator

Add behaviour around an object without changing it, by wrapping it in something of the same type.

Reach for it when: Does this behaviour apply to several implementations, and should it compose with other such behaviour?

The usual mistake: Forgetting that stacking order changes the result, and not deciding deliberately which order you want.

Worked examples:

  • Design a Notification Service (🟡 Medium) — alongside Observer, Factory Method, Strategy
    Fan a message out to email, SMS, push and Slack according to each subscriber's per-channel priority threshold, with retry, rate limiting and deduplication written once as decorators that compose around any channel.

Facade

Offer one simple entry point onto a subsystem that has several parts.

Reach for it when: Does a caller who wants one common thing have to know about four objects to get it?

The usual mistake: Turning it into a god object. A facade delegates; it does not accumulate the logic itself.

Worked examples:

  • Design a Chat Room (🟡 Medium) — alongside Mediator, Observer
    Route messages through rooms so participants never hold references to each other, turning N-squared peer coupling into N — and put blocking, muting and moderation in the mediator, since each is a rule about a relationship neither party should own.
  • Design a Movie Ticket Booking System (🔴 Hard) — alongside Builder, Repository, Proxy, Strategy
    Stop two people buying the same seat by holding it under an expiring lock taken before payment and released after, with a builder assembling the booking, a repository hiding where shows live, and a caching proxy in front of it.
  • Design a Parking Lot (🟡 Medium) — alongside Strategy, Factory Method, Command
    Assign spots to arriving vehicles by a pluggable allocation strategy, charge for the stay on exit by a separate pricing strategy, and drive the whole thing from a command console where adding an operation never edits an existing one.
  • Design a Payment Gateway (🔴 Hard) — alongside Adapter, Abstract Factory, Strategy
    Wrap three payment SDKs that disagree about units, method names and how failure is signalled behind one interface, then use an abstract factory so a region's processor, tax rules and money formatting can never be mixed up.
  • Design a Text Editor with Undo (🟡 Medium) — alongside Command, Memento, Composite
    Build an editor whose every edit is a reversible command, where consecutive keystrokes coalesce into one undo step, replace-all undoes as a single action, and a new edit after an undo correctly discards the redo stack.
  • Design an In-Memory File System (🟡 Medium) — alongside Composite, Visitor, Iterator
    Model files and directories as one composite tree so no caller ever branches on which it is holding, then add search, disk usage and rendering as visitors that walk the tree without living on it.

Factory Method

Decide which concrete class to create in one place, so callers never name it.

Reach for it when: Is the choice of implementation a decision I want made once rather than at every call site?

The usual mistake: A factory that is only ever called with one argument — that is a constructor with extra steps.

Worked examples:

  • Design a Notification Service (🟡 Medium) — alongside Decorator, Observer, Strategy
    Fan a message out to email, SMS, push and Slack according to each subscriber's per-channel priority threshold, with retry, rate limiting and deduplication written once as decorators that compose around any channel.
  • Design a Parking Lot (🟡 Medium) — alongside Strategy, Command, Facade
    Assign spots to arriving vehicles by a pluggable allocation strategy, charge for the stay on exit by a separate pricing strategy, and drive the whole thing from a command console where adding an operation never edits an existing one.

Iterator

Walk a collection without exposing how it is stored.

Reach for it when: Should callers be able to traverse this without knowing it is a dict, a tree, or a generator?

The usual mistake: Materialising the whole collection when a generator would let the caller stop early.

Worked examples:

  • Design an In-Memory File System (🟡 Medium) — alongside Composite, Visitor, Facade
    Model files and directories as one composite tree so no caller ever branches on which it is holding, then add search, disk usage and rendering as visitors that walk the tree without living on it.

Mediator

Route interaction between many objects through one coordinator, so they do not reference each other.

Reach for it when: Would every participant otherwise hold a reference to every other, and who enforces the rules between two of them?

The usual mistake: Confusing it with Observer. A mediator has routing logic; a subject just notifies.

Worked examples:

  • Design a Chat Room (🟡 Medium) — alongside Observer, Facade
    Route messages through rooms so participants never hold references to each other, turning N-squared peer coupling into N — and put blocking, muting and moderation in the mediator, since each is a rule about a relationship neither party should own.

Memento

Capture an object's state so it can be restored later, without exposing its internals.

Reach for it when: Do I need to put something back exactly as it was, and is the state small enough to copy?

The usual mistake: Snapshotting something large on every change, and handing out a mutable snapshot that can be edited after the fact.

Worked examples:

  • Design a Text Editor with Undo (🟡 Medium) — alongside Command, Composite, Facade
    Build an editor whose every edit is a reversible command, where consecutive keystrokes coalesce into one undo step, replace-all undoes as a single action, and a new edit after an undo correctly discards the redo stack.

Observer

Let interested parties subscribe to events without the source knowing who they are.

Reach for it when: Should adding a new listener be registration rather than a code change at the source?

The usual mistake: Using it where a mediator is needed — as soon as the source starts deciding who gets what, it is no longer an observer.

Worked examples:

  • Design a Chat Room (🟡 Medium) — alongside Mediator, Facade
    Route messages through rooms so participants never hold references to each other, turning N-squared peer coupling into N — and put blocking, muting and moderation in the mediator, since each is a rule about a relationship neither party should own.
  • Design a Notification Service (🟡 Medium) — alongside Decorator, Factory Method, Strategy
    Fan a message out to email, SMS, push and Slack according to each subscriber's per-channel priority threshold, with retry, rate limiting and deduplication written once as decorators that compose around any channel.
  • Design an Elevator System (🔴 Hard) — alongside Strategy, State
    Run a bank of elevators where each car follows the LOOK algorithm — sweep one way serving every stop, reverse only when nothing is ahead — and the choice of which car answers a hall call is a strategy that can be tuned without any car noticing.
  • Design Chess (🔴 Hard) — alongside Strategy, Command, Template Method
    Give each piece responsibility for how it moves, make every move an undoable command, and decide legality by playing a move and asking whether your own king is now attacked — verified against known perft counts to depth four.
  • Design Splitwise (🟡 Medium) — alongside Strategy
    Track shared expenses across four split types where every split sums exactly to the total, keep one net balance per person instead of a debt matrix, and settle the whole group in at most n-1 payments.
  • Design Tic Tac Toe (🟢 Easy) — alongside Strategy
    Play on an N x N board with O(1) win detection using per-line counters instead of scanning, and a bot whose difficulty is a swappable strategy — random, or a minimax player that provably never loses.

Proxy

Stand in for another object with the same interface, controlling access to it.

Reach for it when: Do I need caching, lazy loading, access control or logging without the caller noticing?

The usual mistake: Caching reads and forgetting to invalidate on write, which is worse than no proxy because it is confidently wrong.

Worked examples:

  • Design a Movie Ticket Booking System (🔴 Hard) — alongside Builder, Repository, Strategy, Facade
    Stop two people buying the same seat by holding it under an expiring lock taken before payment and released after, with a builder assembling the booking, a repository hiding where shows live, and a caching proxy in front of it.

Repository

Hide where data lives behind a collection-shaped interface.

Reach for it when: Should the rest of the system care whether this comes from memory, a database, or an API?

The usual mistake: Letting query details leak through the interface, so swapping the backing store is no longer possible.

Worked examples:

  • Design a Movie Ticket Booking System (🔴 Hard) — alongside Builder, Proxy, Strategy, Facade
    Stop two people buying the same seat by holding it under an expiring lock taken before payment and released after, with a builder assembling the booking, a repository hiding where shows live, and a caching proxy in front of it.

Singleton

Guarantee exactly one instance of something genuinely global.

Reach for it when: Would two of these be an actual bug, not just wasteful?

The usual mistake: Using it as a global variable. It is almost always the wrong answer, and it always costs testability — provide a reset.

Worked examples:

  • Design a Logging Framework (🟡 Medium) — alongside Chain of Responsibility, Strategy
    Route log records down a handler chain where every link applies its own threshold and format, so one chain can send everything to the console and only errors to a JSON file — and where the chain deliberately does not stop at the first handler that takes it.

State

Put behaviour in an object representing the current state, so transitions replace conditionals.

Reach for it when: Does the same method need to do something different — or be refused — depending on a mode?

The usual mistake: Keeping the state as a flag checked at the top of every method, so adding a state means revisiting all of them.

Worked examples:

  • Design a Vending Machine (🟡 Medium)
    Model a vending machine as an explicit state machine where each state decides which actions it accepts, and where a machine that cannot make change leaves the item on the shelf and the buyer's money untouched.
  • Design an ATM (🟡 Medium) — alongside Chain of Responsibility
    Model an ATM as a state machine where each state permits only what is safe from it, and build the note dispenser as a chain of denomination handlers that plans the full amount before a single note leaves the box.
  • Design an Elevator System (🔴 Hard) — alongside Strategy, Observer
    Run a bank of elevators where each car follows the LOOK algorithm — sweep one way serving every stop, reverse only when nothing is ahead — and the choice of which car answers a hall call is a strategy that can be tuned without any car noticing.

Strategy

Make an algorithm swappable behind a common interface.

Reach for it when: Is there more than one reasonable way to do this, chosen by configuration rather than by the code?

The usual mistake: An if/else re-evaluated on every call. Real Strategy is chosen once and injected.

Worked examples:

  • Design a Logging Framework (🟡 Medium) — alongside Chain of Responsibility, Singleton
    Route log records down a handler chain where every link applies its own threshold and format, so one chain can send everything to the console and only errors to a JSON file — and where the chain deliberately does not stop at the first handler that takes it.
  • Design a Movie Ticket Booking System (🔴 Hard) — alongside Builder, Repository, Proxy, Facade
    Stop two people buying the same seat by holding it under an expiring lock taken before payment and released after, with a builder assembling the booking, a repository hiding where shows live, and a caching proxy in front of it.
  • Design a Notification Service (🟡 Medium) — alongside Decorator, Observer, Factory Method
    Fan a message out to email, SMS, push and Slack according to each subscriber's per-channel priority threshold, with retry, rate limiting and deduplication written once as decorators that compose around any channel.
  • Design a Parking Lot (🟡 Medium) — alongside Factory Method, Command, Facade
    Assign spots to arriving vehicles by a pluggable allocation strategy, charge for the stay on exit by a separate pricing strategy, and drive the whole thing from a command console where adding an operation never edits an existing one.
  • Design a Payment Gateway (🔴 Hard) — alongside Adapter, Abstract Factory, Facade
    Wrap three payment SDKs that disagree about units, method names and how failure is signalled behind one interface, then use an abstract factory so a region's processor, tax rules and money formatting can never be mixed up.
  • Design a Rate Limiter (🟡 Medium)
    Implement token bucket, fixed window, sliding window log and sliding window counter behind one interface, and be able to say what each one gets wrong — starting with the boundary burst that lets a fixed window pass double its limit.
  • Design an Elevator System (🔴 Hard) — alongside State, Observer
    Run a bank of elevators where each car follows the LOOK algorithm — sweep one way serving every stop, reverse only when nothing is ahead — and the choice of which car answers a hall call is a strategy that can be tuned without any car noticing.
  • Design an LRU Cache (🟡 Medium) — alongside Template Method
    Build a fixed-capacity cache with O(1) get and put, backed by a hand-written doubly linked list, whose eviction rule swaps between LRU, LFU and FIFO without the cache learning what any of them mean.
  • Design Chess (🔴 Hard) — alongside Command, Template Method, Observer
    Give each piece responsibility for how it moves, make every move an undoable command, and decide legality by playing a move and asking whether your own king is now attacked — verified against known perft counts to depth four.
  • Design Splitwise (🟡 Medium) — alongside Observer
    Track shared expenses across four split types where every split sums exactly to the total, keep one net balance per person instead of a debt matrix, and settle the whole group in at most n-1 payments.
  • Design Tic Tac Toe (🟢 Easy) — alongside Observer
    Play on an N x N board with O(1) win detection using per-line counters instead of scanning, and a bot whose difficulty is a swappable strategy — random, or a minimax player that provably never loses.
  • Snake and Ladder (🟢 Easy)
    Build a turn-based Snake and Ladder game where the die can be swapped between fair and crooked without touching the game loop, and the board refuses layouts where one jump chains into another.

Template Method

Fix the shape of an algorithm in a base class and let subclasses fill in the steps.

Reach for it when: Do several implementations share a skeleton and differ only in a few steps?

The usual mistake: Reaching for inheritance when the variation is data, not behaviour — if the subclasses differ only in the values they set, those are constructor arguments.

Worked examples:

  • Design an LRU Cache (🟡 Medium) — alongside Strategy
    Build a fixed-capacity cache with O(1) get and put, backed by a hand-written doubly linked list, whose eviction rule swaps between LRU, LFU and FIFO without the cache learning what any of them mean.
  • Design Chess (🔴 Hard) — alongside Strategy, Command, Observer
    Give each piece responsibility for how it moves, make every move an undoable command, and decide legality by playing a move and asking whether your own king is now attacked — verified against known perft counts to depth four.

Visitor

Add operations over a structure without putting them on the structure's classes.

Reach for it when: Do operations keep arriving while the set of node types stays fixed?

The usual mistake: Using it where new node types are the common change — then every visitor has to grow a method, and you have made the expensive case the frequent one.

Worked examples:

  • Design an In-Memory File System (🟡 Medium) — alongside Composite, Iterator, Facade
    Model files and directories as one composite tree so no caller ever branches on which it is holding, then add search, disk usage and rendering as visitors that walk the tree without living on it.