Composite¶
Intent¶
Compose objects into tree structures and treat individual objects and compositions uniformly through a shared interface.
Use When¶
- Your domain is naturally hierarchical (files/folders, UI trees, org charts, expression trees).
- Callers want to run the same operation on a leaf and on a group of leaves.
- You need recursive traversal and aggregation.
Prefer Something Else When¶
- Your structure is not a tree (graph with cycles) unless you can enforce acyclicity.
- You only need traversal (Iterator may be enough).
Minimal Structure¶
Componentinterface (the operations clients use)LeafimplementsComponentCompositeimplementsComponentand storeschildren: List<Component>- Optional:
add/removeonCompositeonly (preferred), or onComponentwith no-op or a typed error result for leaf
Implementation Steps¶
- Define the smallest
Componentinterface that both leaves and composites can support. - Ensure composites delegate the operation to children and aggregate results if needed.
- Decide ownership rules: who can add/remove children and when?
- If cycles are possible, enforce acyclic graphs or add visited-set guards.
Pitfalls¶
- Unsafe child mutation: expose only the operations you need; keep children collection encapsulated.
- Leaf API pollution: don’t force leaves to implement meaningless
add/removeunless your language ecosystem expects it. - Performance: deep recursion may need iterative traversal or caching.
Testing Checklist¶
- Operations behave the same for leaf vs composite.
- Composite aggregation works across nested trees.
- Mutation rules are enforced (e.g., cannot add child where it’s invalid).