Structural Design Patterns
The problem
Existing classes often don't fit together the way new code needs them to — an external library's interface doesn't match what your code expects, or a simple task requires coordinating many subsystems — structural patterns are named ways of composing existing classes into new, useful shapes without modifying them.
Why now
Composition over inheritance already established building objects out of parts as a general strategy; structural patterns are specific, named recipes for that composition, solving a family of exact, recurring shape-mismatch problems instead of reinventing a composition strategy from scratch each time.
Mental model
Every structural pattern composes existing objects to solve one specific shape problem: Adapter translates one interface into another a caller expects, Decorator wraps an object to add behavior without touching its class, Facade hides a complex subsystem behind one simple interface, and Composite treats a group of objects and a single object through the same interface, so client code doesn't need to know which it's dealing with.
Requires
- Composition Over InheritanceLow-Level Design
Every structural pattern achieves its goal by composing objects together rather than through inheritance; you need composition already established as the preferred building strategy before these specific, named compositions make sense as its natural next step.
Unlocks
Used in
- Adapter pattern wrapping a third-party library's API to match your codebase's expected interface
- Decorator pattern for adding cross-cutting behavior (logging, caching) to an object without modifying its class
- Facade pattern simplifying a complex subsystem (e.g. a video conversion library) behind one clean method
- Composite pattern for tree-shaped data (file systems, UI component trees) where a leaf and a group are treated uniformly
Projects
- Implement an Adapter that lets an old PaymentGatewayV1 interface work seamlessly wherever the new PaymentGatewayV2 interface is expected, without modifying either
- Implement a Decorator that adds logging to any object implementing a shared interface, without modifying the wrapped object's class or subclassing it
Examples
- An Adapter wrapping a third-party XML-based library so it satisfies your codebase's JSON-based interface lets you swap the library without touching calling code
- Wrapping a coffee object in a MilkDecorator and then a SugarDecorator adds behavior incrementally without creating a new subclass for every combination
Resources
Mastery checklist
- I can implement an Adapter that reconciles two incompatible interfaces
- I can implement a Decorator that adds behavior to an object without modifying its class
- I can implement a Facade that simplifies a complex subsystem's interface
- I can explain when Composite is appropriate for tree-shaped data