Creational patterns worry about how an object is born. Structural patterns worry about what happens next — how you wire that object together with others into something bigger without the wiring itself becoming the thing you’re afraid to touch.
The failure mode is quiet. A class reaches directly into a third-party type whose interface doesn’t quite match. A feature gets bolted on with a boolean flag, then another, until the combinations explode. A caller is dragged through five collaborators to accomplish one thing. Each is a different way that composition — not creation, not behavior — has calcified.
The smell they all push back on
Here is one ordinary export method. Nothing is broken — it compiles, it ships. But look at how many composition decisions it has nailed down inline, where no caller and no test can reach them.
Three separate kinds of rigidity hide in that method: a mismatched interface adapted by hand, cross-cutting concerns wired with flags, and a subsystem with no front door. Each is a different question — and each has its own structural pattern as the answer.
The map
The Gang of Four grouped these seven together because they all answer how do objects fit together, not how do objects get made (creational) or how do objects collaborate over time (behavioral). Read the family as one decision each moves out of your way.
The seven, at a glance
Each governs one composition decision. Reaching for the wrong one is almost always a sign you’ve misread which decision is actually varying.
- Adapter → — Make an incompatible interface usable without touching either side. Kills the hand-written glue smeared through your code every time a third-party type doesn’t fit. It moves interface matching out of the caller.
- Decorator → — Add responsibilities to one object at runtime, stackably. Kills the subclass explosion (
GzippedEncryptedWatermarkedExporter) and the flag-per-feature branch pile. It moves layered behavior out of the class. - Facade → — Put one simple front door over a complicated subsystem. Kills the caller that has to know the order-of-operations across five collaborators. It moves subsystem orchestration out of the client.
- Proxy → — A stand-in that controls access to the real object — lazy, remote, or protected. Kills the eager load, the leaked network call, the unchecked access sprinkled at every call site. It moves access control out of the caller.
- Composite → — Treat a tree of objects and a single object uniformly. Kills the
if (isLeaf) … else recursethat infects every operation over a hierarchy. It moves the leaf-vs-branch distinction out of the client. - Bridge → — Split an abstraction from its implementation so both vary independently. Kills the combinatorial class blowup when two dimensions (shape × renderer, message × transport) each grow on their own. It moves one axis of variation out of the other.
- Flyweight → — Share immutable intrinsic state across many objects to slash memory. Kills the million near-identical objects that each re-store the same glyph, tile, or particle type. It moves shared state out of the individual instance.
The confusions that cost interviews
Structural patterns look alike on a class diagram — most of them are “an object that holds another object and forwards to it.” The difference is entirely in intent.
Decorator vs Proxy. Both wrap an object of the same interface and delegate to it. The difference is why. A Decorator adds new responsibility the wrapped object didn’t have — compression, a border, a watermark — and you deliberately stack several. A Proxy controls access to an object whose behavior it doesn’t change — it delays creation, checks a permission, or hops the network — and the client ideally never knows it’s there. Decorator enriches; Proxy governs.
Adapter vs Facade vs Bridge. All three sit between a client and something else, so they blur together. But: Adapter changes an interface you don’t own to one you need — it’s reactive glue over an existing, mismatched type. Facade invents a new, simpler interface over a subsystem you do own, hiding its complexity — the subsystem’s real interfaces still exist underneath. Bridge is designed in up front: you deliberately split an abstraction from its implementation so the two can vary on separate axes. Adapter is retrofit, Facade is simplification, Bridge is foresight.
Check yourself
References
Books
- Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, the Structural Patterns chapter.
- Joshua Bloch — Effective Java (3rd ed.), Item 18 (“Favor composition over inheritance”) — the principle the whole family rests on.
- Martin Fowler — Refactoring: Improving the Design of Existing Code, on replacing rigid, hand-wired collaboration with substitutable structure.
Discussion
Loading the conversation…
Discussion
Loading the conversation…