Menu

Earn Premium with Referrals

Invite your friends and earn Premium rewards through our referral program.

See how it works and start inviting friends.

Composite
LLD

Composite

Learn how to treat individual objects and groups of objects uniformly through a tree-like structure.

Composite: Tree Structure Handling

The Problem It Solves

Menus contain items; menus also contain submenus containing more menus and items. GUI panels contain buttons and other panels. Org charts, file systems, bill-of-materials — tree structures everywhere, and client code drowns in if (node instanceof Leaf) ... else recurse into children. Composite removes that branching by giving leaves and containers one shared interface: clients call the same operation on anything in the tree; recursion happens invisibly inside composites.

                 ┌─────────────┐
                 │ «Component» │  operation()     ← ONE interface for all nodes
                 │ + add(c)    │◄───────── clients hold Component refs only
                 │ + remove(c) │
                 └──────△──△───┘
             implements │  │ implements
        ┌───────────────┘  └──────────────┐
 ┌──────┴──────┐                   ┌──────┴────────┐
 │    Leaf     │                   │  Composite    │
 │ MenuItem    │                   │ SubMenu       │
 │ op(): self  │                   │ children list │
 └─────────────┘                   │ op(): for each child → child.op()
                                   └───────────────┘
 RECURSION lives only inside composites — clients never see it

Mechanics

interface MenuComponent {
    void render(StringBuilder sb);
    default void add(MenuComponent c) {
        throw new UnsupportedOperationException("leaf");
    }
}

class MenuItem implements MenuComponent {                  // LEAF
    private final String label;
    MenuItem(String label) { this.label = label; }
    public void render(StringBuilder sb) { sb.append("- ").append(label).append("\n"); }
}

class SubMenu implements MenuComponent {                   // COMPOSITE
    private final String title;
    private final List<MenuComponent> children = new ArrayList<>();
    SubMenu(String title) { this.title = title; }

    public void add(MenuComponent c) { children.add(c); }   // real add/remove here

    public void render(StringBuilder sb) {
        sb.append("+ ").append(title).append("\n");
        for (MenuComponent c : children) c.render(sb);      // uniform recursion
    }
}

Client code becomes structure-agnostic:

MenuComponent main = new SubMenu("Main");
main.add(new MenuItem("Home"));
SubMenu settings = new SubMenu("Settings");   // nested composite — same type
settings.add(new MenuItem("Theme"));
main.add(settings);
main.render(sb);                              // one call walks everything

Design Decisions

QuestionOptions
Where do add/remove live?Component (transparency — leaves get throwing defaults, as above) vs Composite-only (type safety, but clients must downcast)
Child orderingList preserves insertion order; sets when uniqueness matters
Parent pointersNeeded for upward navigation (selection propagation); costs sync maintenance

Real-World Sightings

  • java.awt.Container/Component hierarchy — the pattern’s origin story.
  • JSF/Angular component trees, XML DOM Node, file system walkers.
  • Organization charts and permission trees (Role containing sub-roles).

Operations Beyond Render

Cost aggregation, size calculation, permission checks (“user has any of these roles” over a role tree), serialization — all become single calls recursing through the uniform interface. Adding a new operation across the whole tree still means touching every node class (or pairing with Visitor for stable hierarchies).

Trade-offs & Pitfalls

  • Gains: client simplicity; open-closed growth of tree shapes; recursion centralized.
  • Costs: over-general interfaces (leaves carrying meaningless add); hard to enforce structural constraints (max depth, child types) through the uniform type.
  • Cycle guard: allowing a composite to be added under its own descendant creates infinite recursion — production implementations track ancestors or reject re-parenting.

Interview Framing

  • File-system/menu questions are canonical; producing the shared-interface sketch plus the cycle-guard remark covers pattern and production axes.

My Private Notes

Notes are auto-saved locally to this device.