Composite Pattern

Composite Pattern

Compose objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions of objects uniformly.

Intent

The Composite pattern defines a Component interface (or abstract class) that both Leaf and Composite classes implement. A Leaf node represents a primitive element with no children — it performs the actual work. A Composite node manages a collection of child Component objects and delegates its operation to them, typically iterating over children and aggregating their results. The critical property is uniformity: client code works exclusively through the Component type and never needs to distinguish a leaf from a composite. This allows recursive structures where a composite can contain other composites to arbitrary depth.

Common use cases: file system trees (File/Directory both implement FileSystemNode), UI component trees (Button/Panel both implement Widget), organisation charts (Employee/Department both implement OrgNode), expression abstract syntax trees (Literal/BinaryExpr both implement Expr).

When NOT to Use

  • The structure is flat (no nesting ever occurs) — a plain list or array is clearer and less indirected
  • Leaf and Composite operations are substantially different — a common interface becomes a leaky abstraction that forces unimplemented methods on Leaf or Composite nodes
  • Only one or two levels of nesting exist — the added indirection is not worth it for shallow structures; model explicitly instead
  • Operations on leaves and composites need genuinely different signatures — the common interface cannot accommodate both without becoming awkward

When to Use

  • The structure is naturally hierarchical and recursively nested (trees of arbitrary depth)
  • Clients must treat leaf nodes and container nodes identically — uniform treatment via a common interface is the goal
  • You want to add new kinds of components (new Leaf or Composite types) without changing client code

How It Works

Participants:

  • Component — the interface (or abstract class) that declares the key operation all nodes must implement. Defines the type used by all client code.
  • Leaf — implements Component; has no children; performs the actual work when operation() is called.
  • Composite — implements Component; manages a children: Component[] collection with add(c: Component) and optionally remove(); delegates operation() to each child and aggregates results.
  • Client — uses only the Component type; calls operation() without knowing whether it is talking to a Leaf or a Composite.

The recursive structure is key: because Composite.children is typed as Component[], a child can itself be a Composite, enabling tree structures of arbitrary depth. Composite.operation() typically iterates over children and delegates, making the entire tree compute itself bottom-up.

Class Diagram

classDiagram
    class Component {
        <<interface>>
        +operation()*
    }
    class Leaf {
        +operation()
    }
    class Composite {
        -children : Component[]
        +operation()
        +add(child : Component)
        +remove(child : Component)
        +getChild(index) Component
    }
    Component <|.. Leaf
    Component <|.. Composite
    Composite o-- Component : children (recursive)

TypeScript Example

// Composite — TypeScript
// Source: GoF Design Patterns, p.163
 
interface Component {
  operation(): string;
}
 
class Leaf implements Component {
  constructor(private name: string) {}
  operation(): string { return this.name; }
}
 
class Composite implements Component {
  private children: Component[] = [];
  add(c: Component) { this.children.push(c); }
  operation(): string {
    return `[${this.children.map(c => c.operation()).join(", ")}]`;
  }
}
 
// Usage — client uses only the Component type
const root = new Composite();
root.add(new Leaf("A"));
const branch = new Composite();
branch.add(new Leaf("B"));
branch.add(new Leaf("C"));
root.add(branch);
console.log(root.operation()); // [A, [B, C]]

Java Example

// Composite — Java
// Source: GoF Design Patterns, p.163
interface Component { String operation(); }
 
class Leaf implements Component {
    private final String name;
    Leaf(String n) { this.name = n; }
    @Override public String operation() { return name; }
}
 
class Composite implements Component {
    private final List<Component> children = new ArrayList<>();
    void add(Component c) { children.add(c); }
    @Override public String operation() {
        return children.stream().map(Component::operation)
            .collect(Collectors.joining(", ", "[", "]"));
    }
}

Lineage Forward

The Composite pattern is the structural ancestor of the DDD Aggregate pattern (see Aggregate-Pattern, created Phase 10). An Aggregate is a consistency-boundary cluster over an object graph that shares Composite's part-whole structure but adds invariant enforcement — the Aggregate Root controls all access to the cluster, ensuring the cluster's business rules are never violated by external modification.

PatternRelationship
Flyweight-PatternFlyweight is often used inside Composite leaf nodes to reduce memory cost when many near-identical leaves exist
Decorator-PatternBoth wrap Component objects; Decorator adds behaviour to a single object, Composite manages a tree of objects
Iterator-PatternOften used to traverse a Composite tree (created Phase 9)
Aggregate-PatternDDD Aggregate is a Composite with consistency-boundary semantics (created Phase 10)
  • Atomic-Design-Pattern — Brad Frost's atomic design hierarchy is the Composite pattern applied to UI: atoms are Leaf nodes, molecules and organisms are Composite nodes, the page is the root of the tree. Clients (page layouts) treat atoms and organisms through the same Component interface.
  • Component-Composition-Patterns — Compound components (e.g., <Select><Option>) are a direct implementation of Composite: the parent (Select) manages a collection of children (Option) through shared implicit state via React Context.

Sources

  • Gamma, Helm, Johnson, Vlissides — Design Patterns, Addison-Wesley, 1994, p.163

Notes that link here: GoF-Patterns-MOC