Structural

Composite Pattern

Treat individual objects and tree structures uniformly. An intermediate structural pattern — the foundation of file systems, UI component trees, and any recursive part-whole hierarchy.

Overview ยท Structural ยท Singleton ยท Factory Method ยท Abstract Factory ยท Builder ยท Prototype ยท Adapter ยท Bridge ยท Composite ยท Decorator ยท Facade ยท Flyweight ยท Proxy ยท Observer ยท Strategy ยท Command ยท Template Method ยท Chain of Resp. ยท State ยท Mediator ยท Iterator ยท Visitor ยท Memento ยท Interpreter
Category: Structural Difficulty: Intermediate Interview: Tier 2 Confused with: Decorator
01
Section One ยท The Problem

Why Composite Exists

You are building a file system. A directory can contain files and other directories. You need to calculate the total size: a file returns its own size; a directory returns the sum of all its contents (recursively). The naive approach: check the type at every level with instanceof, handle files one way and directories another, and pray you never add a third type (symlink, archive, virtual mount).

Naive approach — instanceof checks at every level
// โœ— Client must know the type of every node public long calculateSize(Object node) { if (node instanceof File f) { return f.getSize(); } else if (node instanceof Directory d) { long total = 0; for (Object child : d.getChildren()) { total += calculateSize(child); // recursive instanceof } return total; } throw new IllegalArgumentException("Unknown type"); } // โœ— Add Symlink? Another else-if. Add Archive? Another else-if. // โœ— Every operation (size, search, delete) repeats this instanceof tree.

What goes wrong:

  • Type-checking everywhere — every operation (getSize(), search(), delete(), print()) must check instanceof at every node
  • No uniform treatment — the client must handle files and directories with completely different code paths, even though both represent “something in the file system”
  • Open/Closed violation — adding a new node type (symlink, archive) requires editing every operation
  • Recursive logic exposed — the client manages the recursion manually; the tree structure leaks into every method that walks it
Without Composite — client must type-check at every level
calculateSize() instanceof File โ†’ instanceof Directory โ†’ instanceof Symlink โ†’ return f.getSize() loop children recursively follow link? another branch ✗ Every operation repeats this instanceof tree ✗ Client manages recursion manually ✗ New type = edit every operation ✗ No uniform “node” abstraction

This is the problem Composite solves — define a common interface (FileSystemNode) that both files and directories implement. A file returns its own size; a directory sums its children’s sizes. The client calls node.getSize() uniformly on any node — it doesn’t know or care whether it’s a leaf or a branch. The recursion is hidden inside the composite.

02
Section Two ยท The Pattern

What Is Composite?

Composite is a structural pattern that lets you compose objects into tree structures and then treat individual objects and compositions of objects uniformly. The key insight: both a leaf (file) and a branch (directory) implement the same interface. The client calls one method (getSize()) and doesn’t know whether it hit a single object or an entire subtree.

GoF Intent: “Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.”
— Gamma, Helm, Johnson, Vlissides (1994)
Analogy — military unit hierarchy: An army has a general at the top. Under the general: divisions. Under each division: brigades. Under each brigade: platoons. Under each platoon: individual soldiers. When the general gives the order “advance,” the division passes it to brigades, brigades pass it to platoons, platoons pass it to soldiers. The general doesn’t distinguish between a single soldier and an entire division — the command interface is the same at every level: execute(order). A soldier carries it out directly (leaf); a division delegates to its children (composite). Same interface, uniform treatment, recursive structure.

Key insight: Composite is about recursive composition with uniform interface. The leaf does the work directly; the composite delegates to its children. The client never checks types; it just calls the method and lets polymorphism handle the rest. This is why file systems, UI component trees, menu structures, and org charts all use Composite — they’re all part-whole hierarchies where “do X” on a group means “do X on each member.”

Both Leaf and Composite implement the same Component interface
The client treats all nodes uniformly — no instanceof, no type checks
A Leaf implements the operation directly (e.g. File.getSize() returns its own size)
Base case of the recursion — the work actually happens here
A Composite holds children (a List<Component>) and delegates the operation to each child
Recursive step — Directory.getSize() sums children.stream().mapToLong(Component::getSize).sum()
A Composite’s children can be Leaves or other Composites
Arbitrary nesting depth — directories inside directories inside directories, all handled by the same code
Composite vs. Decorator:
  • Both use recursive composition (wrapping objects that share an interface).
  • The difference: Composite is about tree structures with children — one parent, many children, same operation on all.
  • Decorator is about stacking behaviours — one wrapper around one object, adding features.
  • Composite = many children (fan-out). Decorator = one wrapped object (chain).
03
Section Three ยท Anatomy

Participants & Structure

Participant Role In the Analogy
Component The common interface (or abstract class) declaring operations shared by leaves and composites (e.g. getSize(), print()). The client programs to this interface exclusively. The “unit” abstraction — anything that can receive the “advance” command: soldier or division.
Leaf Implements the Component interface directly. Has no children. The base case of the recursion (e.g. File returns its own size). An individual soldier — receives the order and acts on it directly.
Composite Implements the Component interface and holds a collection of children (List<Component>). Its operation delegates to each child (e.g. Directory.getSize() sums children’s sizes). Also provides add() / remove() for managing children. A platoon/brigade/division — passes the order down to all sub-units.
Client Works with the Component interface only. Doesn’t know (or care) whether it’s talking to a Leaf or Composite. Calls component.getSize() on any node. The general — issues one command; the hierarchy handles the rest.
Composite — UML Class Diagram
«interface» FileSystemNode + getSize() : long + print(indent) : void File - name : String - size : long + getSize() โ†’ size Directory - name : String - children : List<FileSystemNode> + getSize() โ†’ sum children + add(FileSystemNode) children * Client uses ■ Blue = Component interface ■ Green = Leaf (File) ■ Gold = Composite (Directory) ◆ filled diamond = composition (children) --▷ dashed = implements Directory’s children can be Files OR other Directories
The self-referential arrow is the key:
  • Notice how Directory holds a List<FileSystemNode> — the same interface it implements.
  • This self-referential relationship allows arbitrary nesting.
  • A Directory’s children can be Files (leaves) or other Directories (composites containing more nodes).
  • The client only ever sees FileSystemNode.
04
Section Four ยท How It Works

The Pattern In Motion

Scenario: A project folder containing source files and sub-directories. Calculate the total size with a single call โ€” the recursion is hidden inside the composite.

Step 1 — Create leaf nodes: File("App.java", 1200), File("README.md", 400), File("Utils.java", 800)
Leaves know their own size. They implement FileSystemNode.getSize() by returning their stored size value.
Step 2 — Create composites: Directory("src"), Directory("project")
Directories start empty. They hold a List<FileSystemNode> and implement getSize() by summing children.
Step 3 — Build the tree: src.add(App), src.add(Utils), project.add(src), project.add(README)
The tree: project/ contains src/ (with 2 files) + README.md. Nesting is arbitrary — same add() for files and directories.
Step 4 — Client calls: project.getSize()
project asks each child for its size. README (leaf) returns 400. src (composite) asks its children: App returns 1200, Utils returns 800 โ†’ src returns 2000. Total: 2400. Client never managed the recursion.
Step 5 — Add a new node type (Symlink) implementing FileSystemNode
The tree, the client, and existing nodes are unchanged. Symlink can be added to any directory. getSize() on the tree still works — polymorphism handles it.
Composite — Tree Traversal (getSize call)
project/ (2400) src/ (2000) README.md (400) App.java (1200) Utils.java (800) project.getSize() โ†’ src.getSize() + README.getSize() โ†’ (App.getSize() + Utils.getSize()) + 400 โ†’ (1200 + 800) + 400 = 2400 ■ Gold = Composite (has children) ■ Green = Leaf (base case)
The pattern in pseudocode
// โ”€โ”€ Build the tree โ”€โ”€ FileSystemNode app = new File("App.java", 1200); FileSystemNode utils = new File("Utils.java", 800); FileSystemNode readme = new File("README.md", 400); Directory src = new Directory("src"); src.add(app); src.add(utils); Directory project = new Directory("project"); project.add(src); project.add(readme); // โ”€โ”€ One call โ€” recursion hidden inside the composite โ”€โ”€ System.out.println(project.getSize()); // โ†’ 2400 project.print(""); // project/ // src/ // App.java (1200 bytes) // Utils.java (800 bytes) // README.md (400 bytes)
The client never checks types:
  • Notice how the client calls project.getSize() and gets the recursive total.
  • It never asks “are you a file or directory?” — it just calls the interface method.
  • The composite pattern hides the recursion inside Directory.getSize(), which iterates its children list and sums their sizes.
  • Each child either returns its own size (leaf) or recursively sums its children (composite).
  • One method call, uniform treatment, arbitrary depth.
05
Section Five ยท Java Stdlib

You Already Use This

Composite is everywhere in Java — any tree structure where you treat leaves and branches the same way uses this pattern.

IN JAVA
Example 1 java.awt.Container — the textbook Composite. A Container (JPanel, JFrame) holds child Components (JButton, JLabel). Both Container and Component share the same interface: paint(), setVisible(), getPreferredSize(). Calling panel.paint(g) recursively paints all children. A JPanel inside a JFrame inside another JPanel — arbitrary nesting, uniform treatment.
Example 2 java.io.File (legacy) / java.nio.file.Path — a File represents both files (leaves) and directories (composites). listFiles() returns children for directories. The newer Files.walk(path) traverses the tree uniformly. While not a pure GoF Composite (no shared getSize() method), the part-whole hierarchy structure is identical.
Example 3 javax.swing.JComponent / javax.faces.component.UIComponent — JSF’s component tree is pure Composite. Every UIComponent can have children (getChildren()) or be a leaf. Rendering calls encodeAll() which recursively renders the entire subtree. JSF pages are component trees processed uniformly.
Stdlib usage — AWT Container as Composite
// Container (composite) holds Components (leaves or other containers) JFrame frame = new JFrame("App"); // composite JPanel panel = new JPanel(); // composite (nested) JButton btn = new JButton("OK"); // leaf JLabel label = new JLabel("Hi"); // leaf panel.add(btn); // add leaf to composite panel.add(label); // add leaf to composite frame.add(panel); // add composite to composite // Uniform operation โ€” paints entire tree recursively frame.setVisible(true); // frame.paint() โ†’ panel.paint() โ†’ btn.paint() + label.paint()
Stdlib usage — Files.walk() traversing a composite file tree
// Walk a directory tree โ€” treats files and directories uniformly long totalSize = Files.walk(Path.of("/project")) .filter(Files::isRegularFile) .mapToLong(p -> p.toFile().length()) .sum(); System.out.println("Total: " + totalSize + " bytes");
Swing’s entire UI is a Composite tree:
  • When you call frame.repaint(), it propagates down through every nested panel, button, and label.
  • Each component either paints itself (leaf) or paints itself then asks its children to paint (composite).
  • This is why you can nest JPanels inside JPanels inside JFrames to any depth — it’s Composite all the way down.
06
Section Six ยท Implementation

Build It Once

Domain: File System. A FileSystemNode interface, a File leaf, and a Directory composite with getSize() and print() operations.

Java — Composite Pattern File System (core)
// โ”€โ”€ Component interface โ”€โ”€ interface FileSystemNode { long getSize(); void print(String indent); } // โ”€โ”€ Leaf โ”€โ”€ record File(String name, long size) implements FileSystemNode { public long getSize() { return size; } public void print(String indent) { System.out.println(indent + name + " (" + size + " bytes)"); } } // โ”€โ”€ Composite โ”€โ”€ class Directory implements FileSystemNode { private final String name; private final List<FileSystemNode> children = new ArrayList<>(); public void add(FileSystemNode node) { children.add(node); } public long getSize() { return children.stream().mapToLong(FileSystemNode::getSize).sum(); } public void print(String indent) { System.out.println(indent + name + "/"); children.forEach(c -> c.print(indent + " ")); } }
Why record for File?:
  • In the core snippet above, File is a Java record — it’s immutable, has no children, and its state is just name + size.
  • Records are perfect for Composite leaves because leaves are data objects with no structure management.
  • Composites (Directory) are regular classes because they manage mutable children lists.
07
Section Seven ยท Watch Out

Common Mistakes

Mistake #1 — Putting add()/remove() on the Component interface: The GoF shows add(child) on the Component interface for “transparency” — so the client doesn’t need to distinguish leaves from composites. But this means calling file.add(child) on a leaf must throw an exception or silently fail. Modern consensus: put add()/remove() only on the Composite (Directory). This sacrifices some transparency for type safety — the compiler tells you a File can’t have children.
✗ Wrong — add() on Component (leaf must throw)
// โœ— File forced to implement add() with a useless exception interface FileSystemNode { void add(FileSystemNode n); // leaf can't do this! long getSize(); } class File implements FileSystemNode { public void add(FileSystemNode n) { throw new UnsupportedOperationException(); // runtime bomb } }
✔ Correct — add() only on Directory
// โœ“ Component has only shared operations; add() lives on Directory interface FileSystemNode { long getSize(); void print(String indent); } class Directory implements FileSystemNode { public void add(FileSystemNode n) { children.add(n); } // only here }
Mistake #2 — No protection against cycles:
  • If you accidentally add a directory as its own child (dir.add(dir)) or create a circular reference, getSize() will recurse infinitely โ†’ StackOverflowError.
  • Fix: Check for cycles in add(): if (node == this || isAncestor(node)) throw new IllegalArgumentException("Cycle detected").
Mistake #3 — Exposing the children list directly:
  • If Directory.getChildren() returns the internal List, external code can bypass add()/remove() and mutate the tree without validation.
  • Fix: Return Collections.unmodifiableList(children) or an iterator — never the raw mutable list.
Mistake #4 — Confusing Composite with Decorator:
  • Both use recursive composition with a shared interface.
  • The difference: Composite is a tree (one parent, many children) where an operation on a group means “do it on each child.” Decorator is a chain (one wrapper around one object) where each layer adds behaviour.
  • If your structure has one child per node, that’s Decorator.
  • If it has many children per node, that’s Composite.
08
Section Eight ยท Decision Guide

When To Use Composite

Use Composite When
  • You have a part-whole hierarchy — objects that contain other objects of the same type: file systems, org charts, UI component trees, menu structures
  • You want clients to treat individual objects and groups uniformly — the same operation (size, render, execute) applies to both leaves and branches
  • You need recursive structures where nesting depth is arbitrary or unknown at compile time
  • You want to add new node types (e.g. symlink, archive) without modifying existing code — just implement the Component interface
  • Operations on a group mean “do it on each member” — size of a directory = sum of children’s sizes, rendering a panel = render each child widget
Avoid Composite When
  • Your structure is flat — if there’s no nesting (just a list of items), a simple collection is enough; Composite adds unnecessary abstraction
  • Leaf and Composite operations are fundamentally different — if the behaviour of a group differs drastically from an individual, forcing them into the same interface leads to awkward no-op methods
  • You need to stack behaviours on a single object (one wrapper, one child) — that’s Decorator, not Composite
  • You need different traversal strategies — Composite hard-codes the traversal inside the composite node; consider Iterator or Visitor if you need multiple traversal algorithms
  • Your hierarchy is fixed and shallow (e.g. always exactly 2 levels) — the overhead of the pattern isn’t justified
Composite vs. Confused Patterns
Pattern Purpose Structure When to pick it
Composite ← this Treat trees uniformly One parent, many children Part-whole hierarchy, recursive operations
Decorator Add behaviour to same interface One wrapper, one wrapped object Stack features without subclassing
Iterator Traverse a collection External cursor over elements Need multiple traversal strategies on the same tree
Visitor Add operations without modifying classes Double dispatch on node types Many different operations on a stable set of node types
Chain of Responsibility Pass request along a chain Linear chain of handlers One handler processes the request; not recursive aggregation
Decision Flowchart
Objects form a tree / part-whole hierarchy? Yes No Simple collection Same operation on leaf & group? No Visitor / Strategy Yes Many children per node (fan-out)? No (one) Decorator Yes Composite
09
Section Nine ยท Practice

Problems To Solve

Composite problems test whether you can identify the Component, Leaf, and Composite roles, implement recursive delegation correctly, and handle edge cases like empty composites and deep nesting.

Difficulty Problem Key Insight
Easy Menu System
Build a restaurant menu where a MenuItem (leaf) has a name and price, and a SubMenu (composite) holds a collection of menu entries. Implement getPrice() that returns the item price for a leaf and sums all children’s prices for a sub-menu. Also implement print(indent) that renders the menu tree with indentation.
The simplest Composite exercise: one interface (MenuEntry), one leaf, one composite. The getPrice() method mirrors getSize() in the file system example — leaf returns its own value, composite sums its children. Tests basic recursive delegation and uniform treatment.
Easy Organization Chart
Model an organization where an Employee (leaf) has a name and salary, and a Department (composite) contains employees and sub-departments. Implement getTotalSalary() and countEmployees() across the entire tree. Ensure Department.countEmployees() counts only leaves, not sub-departments.
Tests Composite with two operations on the same tree. getTotalSalary() is a straightforward sum; countEmployees() requires distinguishing leaf-count vs. node-count. A leaf returns 1 for count and its salary for total; a composite sums both from children.
Medium Expression Evaluator
Build an arithmetic expression tree. A NumberExpression (leaf) holds a numeric value. An OperatorExpression (composite) holds an operator (+, −, ×, ÷) and two child expressions. Implement evaluate() that recursively computes the result, and toString() that produces a fully-parenthesized infix string like ((3 + 5) * 2).
Tests Composite in a non-collection context — the composite has exactly two children (left, right) instead of a variable-length list. Also introduces operator-specific logic in the composite node, which is more complex than simple aggregation. The toString() method tests recursive structural rendering.
Medium Permission System
A Permission interface has hasAccess(String resource) : boolean. A SinglePermission (leaf) grants access to one specific resource. A PermissionGroup (composite) holds child permissions and grants access if any child grants it (OR logic). Add a second composite, AllRequiredGroup, that grants access only if all children grant it (AND logic). Build a tree and test complex permission queries.
Tests Composite with boolean aggregation (OR vs. AND) instead of numeric summation. Having two different composite types that aggregate differently is a real-world scenario (role-based access). The client calls hasAccess() uniformly — it doesn’t know whether the tree uses OR, AND, or a mix of both.
Hard UI Layout Engine
Build a simplified UI layout system. A Widget interface has render(x, y) and getPreferredSize() : Dimension. Leaf widgets: Button, Label, TextInput. Composite widgets: HBox (lays children out horizontally), VBox (vertically). Each composite computes its preferred size from children (HBox: sum widths, max height; VBox: max width, sum heights). Implement render() that assigns positions to each child based on the layout strategy.
Tests Composite with layout computation — the composite doesn’t just aggregate a value but must compute positions for each child before delegating render(). Two different composite strategies (HBox vs. VBox) show how composites can vary their delegation logic while maintaining the same interface. This mirrors real UI frameworks (Swing, JavaFX, Flutter).
Interview Tip:
  • When asked about Composite, the interviewer wants to see: (1) a clear Component interface with operations shared by all nodes; (2) a Leaf that implements the operation directly (base case); (3) a Composite that delegates to children and aggregates results (recursive case); (4) that add()/remove() belong on the Composite, not on the Component; (5) awareness that AWT/Swing Container/Component is the canonical Java example.
  • Stand-out answers distinguish Composite from Decorator (many children vs. one wrapper), and mention cycle prevention and immutable children lists as production concerns.