Understand what Java structural patterns solve, when they beat inheritance, and how to choose among wrappers, bridges, composites, decorators, facades, and proxies.
Structural patterns: Patterns that organize how classes and objects are connected so a system can adapt interfaces, layer behavior, hide complexity, or model part-whole relationships cleanly.
Java uses structural patterns constantly, even when teams do not name them that way. InputStream wrappers, HTTP filters, ORM facades, remote clients, and tree-shaped UI models all carry structural decisions. The pattern names help when they sharpen those decisions. They hurt when they become diagram-first ceremony.
Structural patterns matter when the friction is in the relationships between types:
That is different from creational patterns, which focus on how objects come into existence, and from behavioral patterns, which focus on how responsibility moves through the system.
flowchart LR
A["Structural pressure"] --> B["Interface mismatch"]
A --> C["Two dimensions of change"]
A --> D["Part-whole tree"]
A --> E["Layered optional behavior"]
A --> F["Complex subsystem boundary"]
A --> G["Controlled access"]
B --> H["Adapter"]
C --> I["Bridge"]
D --> J["Composite"]
E --> K["Decorator"]
F --> L["Facade"]
G --> M["Proxy"]
Java adds a few forces that shape how these patterns are implemented:
Because of that, the Java version of a structural pattern is rarely just a direct copy of the original catalog drawing.
Use Adapter when the client already has the right contract and one dependency needs translation.
Use Bridge when you have two axes of variation and you want them to evolve independently.
Use Composite when recursive structure matters more than type distinction.
Use Decorator when optional behavior should be added by wrapping, not by creating more subclasses.
Use Facade when the subsystem boundary is too noisy.
Use Proxy when access, laziness, security, or remoteness must be controlled.
Use Flyweight when large numbers of similar objects make intrinsic and extrinsic state separation worthwhile.
Structural patterns go wrong in predictable ways:
The fix is not “avoid patterns.” The fix is to make the structural reason explicit and keep the boundary narrow.
Before implementing a structural pattern in Java, ask:
Structural patterns are strong when they make the object graph easier to reason about. They are weak when they only make the diagram look more sophisticated.