Composite
Composite arranges objects into trees for part-whole hierarchies and lets clients treat leaves and containers uniformly through a shared interface. Over-general interfaces are its main risk.
The Composite pattern composes objects into tree structures to represent part-whole hierarchies and lets clients treat individual objects and groups of objects uniformly. It solves a common awkwardness: when code must handle both single items and collections of items, conditional logic to distinguish "leaf" from "container" spreads everywhere. Composite removes that distinction by giving both a shared interface.
How It Works
A Component interface declares operations common to both simple and complex elements. A Leaf is a primitive element with no children that implements the operations directly. A Composite holds child Components (leaves or other composites) and implements the operations by delegating to and aggregating over its children, often recursively.
Because a composite's children are themselves Components, the structure is recursive and can nest to any depth. Client code calls an operation on the root Component and the call propagates down the tree without the client knowing whether any node is a leaf or a subtree. A design decision is where to place child-management methods (add, remove): on the Component for transparency, or only on the Composite for type safety.
When to Use It
Use Composite when you need to represent part-whole hierarchies and want clients to ignore the difference between individual objects and compositions. It fits file systems (files and folders), GUI widget trees (controls and containers), organization charts, document object models, and nested menus.
It is a poor fit when the elements do not share meaningful common operations; forcing an interface that leaves implement awkwardly (with no-op or exception-throwing methods for child management) is a sign the abstraction is wrong.
Trade-offs
Composite simplifies client code by offering a uniform interface over trees, makes adding new component types easy, and naturally expresses recursive structures. Operations over the whole hierarchy become simple recursive calls.
The trade-off is that a single shared interface can become overly general: leaf objects may be forced to implement child-management methods that do not apply, which either weakens type safety (transparent design) or breaks uniformity (safe design). Deep trees can also be hard to constrain — it can be difficult to restrict which component types a composite may contain — and very large structures can have performance and stack-depth implications for recursive traversal.
Related Patterns
Composite is frequently combined with Iterator to traverse the tree and with Visitor to apply operations across nodes without bloating the component interface. Decorator has a similar recursive structure but adds responsibilities to a single component rather than aggregating many. Builder is handy for constructing complex composite trees.
Example
A drawing application models graphics as a Graphic component. Line and Circle are leaves; Group is a composite holding child graphics. Calling draw() or move(dx, dy) on a group recursively applies it to every child. The user can group shapes, nest groups, and manipulate a whole assembly as if it were a single shape — the rendering code calls one method on the root and the tree handles the rest.