Part of my notebook. Terse by design. Code examples for every GoF pattern live in iluwatar/java-design-patterns.

Principles first

  • DRY: don’t repeat yourself.
  • KISS: keep it simple and stupid.
  • YAGNI: you ain’t gonna need it. The most violated of the three.

SOLID:

  • Single responsibility: a class has one reason to change.
  • Open-closed: open for extension, closed for modification.
  • Liskov substitution: any child class can stand in for its parent.
  • Interface segregation: no client forced to implement methods it doesn’t need; prefer many small client-specific interfaces.
  • Dependency inversion: high-level modules and low-level modules both depend on abstractions; details depend on abstractions, never the reverse.

The GoF catalog

Why patterns at all: shared vocabulary, reusable shapes, faster debugging of other people’s code. Why caution: applying them where they’re not needed is how simple code dies.

Creational

  • Singleton: one instance per JVM. (And one global variable per regret.)
  • Factory: a superclass with multiple subclasses; instantiation moves from client code into a factory that picks the subclass from input.
  • Abstract factory: factory of factories; replaces the factory’s if/else with one factory class per family.
  • Builder: step-by-step construction for objects drowning in optional parameters; solves the telescoping constructor and inconsistent-state problems.
  • Prototype: when construction is expensive, clone an existing instance and modify. The class itself must own the copying (shallow vs deep is a per-case decision).

Structural

  • Adapter: makes two incompatible interfaces work together, like a mobile charger between the 240V socket and a 3V battery.
  • Bridge: decouples abstraction from implementation so both vary independently (one remote, many devices).
  • Composite: part-whole trees where leaves and containers are treated uniformly; color the drawing, and every shape inside gets colored.
  • Decorator: add behavior to an object at runtime without touching its interface.
  • Facade: a simple front door to a complicated subsystem; the client stops caring which database and report engine are behind it.
  • Flyweight: share fine-grained immutable objects to save memory; Java’s String pool.
  • Proxy: a surrogate controlling access to the real object: permission checks, lazy loading, or standing in for something heavy.

Behavioral

  • Template method: fix the skeleton of an algorithm (foundation before windows), let subclasses fill in steps.
  • Mediator: central hub for object communication; air traffic control instead of pilots negotiating with each other.
  • Chain of responsibility: pass the request along handlers until one takes it; how catch blocks work.
  • Observer: subjects notify observers of state changes.
  • Strategy: interchangeable algorithms chosen at runtime; Collections.sort() taking a Comparator.
  • Command: encapsulate a request as an object between invoker and receiver; undo stacks, task queues.
  • State: behavior changes with internal state, without the if/else ladder.
  • Visitor: move an operation over a family of objects into its own class; the shopping cart totaler.
  • Interpreter: define a grammar and interpret it; compilers, translators.
  • Iterator: standard traversal without exposing the underlying structure.
  • Memento: save and restore an object’s state without breaking encapsulation; undo, again.

Microservices patterns

Source and further reading: microservices.io.

Database per service: each database owned by exactly one service, no sharing. Everything below exists because of this rule.

Saga: business transactions spanning services (order + credit limit in different databases) can’t use local ACID transactions, and 2PC is usually off the table. A saga is a sequence of local transactions, each publishing an event that triggers the next; on failure, compensating transactions undo the preceding steps.

Event sourcing: persist entities as sequences of state-changing events; appending one event is atomic. Rebuild current state by replaying; checkpoint with periodic snapshots so you replay less. The event store doubles as a message broker (services subscribe to events). Distinction worth keeping: sagas handle workflow, event sourcing is how state is stored.

CQRS: with per-service databases (and especially with event sourcing), cross-service queries stop being straightforward. Separate the command side (writes) from the query side: maintain read-only view databases kept current by subscribing to domain events. Benefits: denormalized views that scale, cleaner separation. Costs: more moving parts, duplicated code, replication lag, eventual consistency in views.

The fallacies, again

The eight fallacies of distributed computing apply doubly to microservices, since every design above assumes the network between services misbehaves. Read them before applying anything here.