Composite Pattern
IntermediateComposite lets clients treat individual objects and compositions of objects uniformly — perfect for tree structures like file systems and UI hierarchies.
Overview
The Composite pattern composes objects into tree structures to represent part-whole hierarchies. Clients interact with a uniform Component interface — they do not need to know whether they are dealing with a leaf (single item) or a composite (container of items). Java examples: java.awt.Container/Component (Swing UI tree), javax.swing component hierarchy, and XML/JSON document models.
Composite Pattern Structure
Three participants: Component (interface/abstract class with common operations), Leaf (implements Component, no children), Composite (implements Component, holds children, delegates to them).
The key insight: both Leaf and Composite implement the same interface. Client code operates on Component — it does not care whether it holds a single item or a whole subtree.
// Component interface
public interface FileSystemItem {
String name();
long size();
void print(String indent);
}
// Leaf
public record File(String name, long size) implements FileSystemItem {
@Override public void print(String indent) {
System.out.printf("%s📄 %s (%,d bytes)%n", indent, name, size);
}
}
// Composite
public class Directory implements FileSystemItem {
private final String name;
private final List<FileSystemItem> children = new ArrayList<>();
public Directory(String name) { this.name = name; }
public void add(FileSystemItem item) { children.add(item); }
public void remove(FileSystemItem item) { children.remove(item); }
@Override public String name() { return name; }
@Override public long size() {
return children.stream().mapToLong(FileSystemItem::size).sum();
}
@Override public void print(String indent) {
System.out.printf("%s📁 %s (%,d bytes)%n", indent, name, size());
children.forEach(c -> c.print(indent + " "));
}
}Using the Composite
Client code builds the tree and operates on it through the Component interface. Operations like size(), print(), and search() are defined once on the interface and work recursively throughout the hierarchy.
This is the power of Composite: adding a new operation means adding it to the Component interface — it automatically works for both leaves and composites.
// Build a file system tree
Directory root = new Directory("root");
Directory src = new Directory("src");
Directory test = new Directory("test");
src.add(new File("Main.java", 2048));
src.add(new File("UserService.java", 4096));
src.add(new File("OrderService.java",3072));
test.add(new File("MainTest.java", 1024));
test.add(new File("UserServiceTest.java", 2048));
root.add(src);
root.add(test);
root.add(new File("pom.xml", 512));
root.add(new File("README.md", 256));
// Client treats everything as FileSystemItem — uniform interface
root.print("");
// 📁 root (12,056 bytes)
// 📁 src (9,216 bytes)
// 📄 Main.java (2,048 bytes)
// 📄 UserService.java (4,096 bytes)
// 📄 OrderService.java (3,072 bytes)
// 📁 test (3,072 bytes)
// 📄 pom.xml (512 bytes)
// 📄 README.md (256 bytes)
System.out.printf("Total: %,d bytes%n", root.size()); // 12,056
// Recursive search — works on any node
long javaSize = src.children().stream()
.filter(i -> i.name().endsWith(".java"))
.mapToLong(FileSystemItem::size)
.sum();Composite in Java Swing and Real-World Uses
Swing's component hierarchy is the classic JDK Composite: Container extends Component; every Container can hold other Components (including other Containers). paintComponent() is defined on Component — it works correctly for both leaf widgets and composite panels.
Other uses: expression trees (compilers/parsers), org chart / reporting hierarchies, menu systems, and HTML DOM representation.
// Swing — JDK's Composite pattern
JFrame frame = new JFrame("App"); // Composite
JPanel topPanel = new JPanel(); // Composite
JButton button = new JButton("Click"); // Leaf
JLabel label = new JLabel("Hello"); // Leaf
JPanel nested = new JPanel(); // Composite inside Composite
nested.add(button);
nested.add(label);
topPanel.add(nested);
frame.add(topPanel);
// Every component responds to the same Component API:
// getSize(), repaint(), setVisible(), etc.
// Expression tree (mini-compiler)
interface Expr { int evaluate(); }
record Num(int value) implements Expr {
public int evaluate() { return value; }
}
record Add(Expr left, Expr right) implements Expr {
public int evaluate() { return left.evaluate() + right.evaluate(); }
}
record Mul(Expr left, Expr right) implements Expr {
public int evaluate() { return left.evaluate() * right.evaluate(); }
}
// (2 + 3) * 4
Expr tree = new Mul(new Add(new Num(2), new Num(3)), new Num(4));
System.out.println(tree.evaluate()); // 20Key Points to Remember
- Composite allows clients to treat Leaf and Composite objects uniformly via a Component interface.
- Composite delegates operations to its children recursively.
- Swing's Container/Component hierarchy is the classic JDK example of Composite.
- Operations defined on the Component interface automatically work for the entire tree.
- Use Composite when you have part-whole hierarchies (file systems, UI trees, expression trees).
Practice Composite Pattern in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat problem does the Composite pattern solve?
What are the three participants in the Composite pattern?
How does Swing use the Composite pattern?
What is the difference between Composite and Decorator patterns?
How would you implement a recursive size() operation using Composite?
Ask Aria about Composite Pattern
Your personal AI tutor — ask anything about this concept