Composite Pattern
IntermediateComposes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.
Overview
The Composite pattern models tree structures where leaf nodes and composite nodes are treated identically via a common Component interface. The key insight is that a Composite contains a list of Component children — each child can itself be a Composite or a Leaf. This enables recursive operations (compute total size, render all nodes) without the client needing to distinguish leaves from composites. Classic examples: file system (File is leaf, Directory is composite), UI component trees (Button is leaf, Panel is composite), arithmetic expression trees, org charts.
File System Example
FileSystemEntry is the Component interface. File is the Leaf — it has no children. Directory is the Composite — it holds a list of FileSystemEntry children and delegates size() to each recursively.
import java.util.ArrayList;
import java.util.List;
// Component interface
public interface FileSystemEntry {
String getName();
long size(); // recursive for directories
void print(String indent);
}
// Leaf — has no children
public class File implements FileSystemEntry {
private final String name;
private final long sizeBytes;
public File(String name, long sizeBytes) {
this.name = name;
this.sizeBytes = sizeBytes;
}
@Override public String getName() { return name; }
@Override public long size() { return sizeBytes; }
@Override
public void print(String indent) {
System.out.println(indent + "📄 " + name + " (" + sizeBytes + " bytes)");
}
}
// Composite — contains children (Files or Directories)
public class Directory implements FileSystemEntry {
private final String name;
private final List<FileSystemEntry> children = new ArrayList<>();
public Directory(String name) { this.name = name; }
public void add(FileSystemEntry entry) { children.add(entry); }
public void remove(FileSystemEntry entry) { children.remove(entry); }
@Override public String getName() { return name; }
@Override
public long size() {
return children.stream()
.mapToLong(FileSystemEntry::size) // recursive
.sum();
}
@Override
public void print(String indent) {
System.out.println(indent + "📁 " + name + "/ (" + size() + " bytes)");
children.forEach(child -> child.print(indent + " "));
}
}
// Building the tree
Directory root = new Directory("root");
Directory src = new Directory("src");
src.add(new File("Main.java", 1024));
src.add(new File("Config.java", 512));
Directory resources = new Directory("resources");
resources.add(new File("application.yml", 256));
root.add(src);
root.add(resources);
root.add(new File("README.md", 128));
root.print("");
System.out.println("Total size: " + root.size() + " bytes"); // 1920Expression Tree
Composite naturally models arithmetic expression trees. Number is a Leaf; Addition and Multiplication are Composites. evaluate() recursively computes the result.
// Component
public interface Expression {
int evaluate();
}
// Leaf
public class Number implements Expression {
private final int value;
public Number(int value) { this.value = value; }
@Override public int evaluate() { return value; }
}
// Composite — binary operation
public class Addition implements Expression {
private final Expression left, right;
public Addition(Expression left, Expression right) {
this.left = left; this.right = right;
}
@Override public int evaluate() { return left.evaluate() + right.evaluate(); }
}
public class Multiplication implements Expression {
private final Expression left, right;
public Multiplication(Expression left, Expression right) {
this.left = left; this.right = right;
}
@Override public int evaluate() { return left.evaluate() * right.evaluate(); }
}
// (3 + 4) * (2 + 5) = 49
Expression expr = new Multiplication(
new Addition(new Number(3), new Number(4)),
new Addition(new Number(2), new Number(5)));
System.out.println(expr.evaluate()); // 49Key Points to Remember
- 1Composite lets clients treat Leaf and Composite nodes uniformly through the Component interface.
- 2The Composite holds a list of Component children — each may be a Leaf or another Composite (recursion).
- 3size(), evaluate(), render() methods are naturally recursive in Composite structures.
- 4The Component interface should not expose child management (add/remove) — that belongs only on Composite.
- 5Real-world: javax.swing.JComponent (UI tree), XML DOM, org charts, JSON/YAML object trees.
Interview Questions
Sign in to ask AriaWhat problem does the Composite pattern solve?
Should add() and remove() be on the Component interface or only on Composite?
How does Composite relate to the Visitor pattern?
Implement a menu system (MenuItem and Menu) using Composite.
Ask Aria about Composite Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.