About gof-composite
gof-composite is a skill that explains the Composite pattern for representing part-whole hierarchies. It helps developers assess when leaves and branches should share an interface, including the safety and recursion trade-offs.
Key features
- 01Component interface for leaves and branches
- 02Recursive operation delegation
- 03Transparent and safe Composite trade-offs
- 04Selection criteria and anti-patterns
Use cases
- 01Modeling files and directories
- 02Building nested menus or GUI components
- 03Deciding whether a hierarchy needs Composite
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-compositeAfter installing, type/gof-compositein Claude Code to confirm it loaded.
Details
From the project READMEOne-line summary
Compose objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects (leaves) and compositions of objects (branches) uniformly.
When to use this skill
- A part-whole hierarchy: file system (files and directories), menus and submenus, GUI components (button vs panel of components), organizational hierarchies.
- Client code needs to call the same operations on both leaves and branches and not care which it has.
- Recursive structures where the recursion shouldn't show in the calling code.
When NOT to use this skill
- A flat collection — there's no hierarchy, just use a list.
- The operations are genuinely different for leaves vs. branches and forcing them through one interface awkwardly buries the distinction.
- Two-level structures with only one layer of grouping — sometimes a
List<Item>is enough without inventing a Composite.
Core content
The pattern introduces a common interface (Component) shared by leaves and composites. Both implement the operations the client cares about; composites implement them by delegating to their children.
Component { operation() }
Leaf { operation() /* does the work */ }
Composite { children: List<Component>
operation() { children.forEach { it.operation() } }
add(c); remove(c); }
Trade-off about transparency vs safety:
- Transparent Composite —
Componentdeclaresadd(child). Both leaves and composites have the method. Clients can treat them uniformly butLeaf.add(child)either no-ops or throws. - Safe Composite —
add(child)lives only onComposite. Type-safe but clients must check the type before adding.
HFDP favors transparent in its examples; in practice, safe is often cleaner unless uniform handling really matters.
Decision heuristics
- Composite is worth its weight when all the operations meaningfully apply to both leaves and branches.
- If only one operation applies uniformly and the rest differ, the abstraction is forced — consider whether you need Composite at all.
- For very deep or very wide trees, watch out for stack-recursive operations exploding the stack — flatten with an Iterator if needed.
Anti-patterns
- Composite with operations that only make sense on leaves, no-oped on composites (or vice versa) — uniformity is performative, not real.
- "Composite" that's really a list — no recursion, no branches with children. It's just a collection.
- Composite that exposes its children directly via
getChildren()and lets callers mutate the tree externally — destroys invariants.
See also
gof-iterator— the natural way to walk a Composite tree.gof-decorator— same structural shape (both wrap), different intent (Decorator adds behavior; Composite represents part-whole).gof-kotlin-idioms— sealed class hierarchies (sealed interface Component { class Leaf; class Branch(val children: List<Component>) }) plus exhaustivewhenmake Composite explicit and type-safe.
References
- Head First Design Patterns (2nd ed), Ch. 9 ("Well-Managed Collections: the Iterator and Composite Patterns") — Composite is illustrated with menus-of-menus.