Compose objects into tree structures to represent part-whole hierarchies. Treat individual objects and compositions uniformly.
The Composite pattern lets you compose objects into tree structures and then treat individual objects (leaves) and groups of objects (composites) through the same interface.
Think of a file system: you can call getSize() on a single file or on a whole folder — the folder recursively sums up all its children.
Without Composite, every operation needs branching logic to handle leaves vs containers separately, breaking the Open/Closed Principle.
// Without Composite: manual checks everywhere if (item instanceof File) return item.size; else if (item instanceof Directory) return item.sumChildren(); // With Composite: recursive polymorphism return item.getSize();
// Composite Pattern — File System Tree
// ─── Component interface ──────────────────────────────────────
interface FileSystemItem {
String getName();
long getSize();
void display(String indent);
}
// ─── Leaf ─────────────────────────────────────────────────────
class File implements FileSystemItem {
private String name;
private long size;
public File(String name, long size) { this.name = name; this.size = size; }
public String getName() { return name; }
public long getSize() { return size; }
public void display(String indent){ System.out.println(indent + "📄 " + name + " (" + size + "KB)"); }
}
// ─── Composite ────────────────────────────────────────────────
class Directory implements FileSystemItem {
private String name;
private java.util.List<FileSystemItem> children = new java.util.ArrayList<>();
public Directory(String name) { this.name = name; }
public void add(FileSystemItem item) { children.add(item); }
public String getName() { return name; }
// Size = sum of all children — works recursively
public long getSize() {
return children.stream().mapToLong(FileSystemItem::getSize).sum();
}
public void display(String indent) {
System.out.println(indent + "📁 " + name + " (" + getSize() + "KB total)");
for (FileSystemItem child : children) {
child.display(indent + " "); // recurse
}
}
}
// ─── Client — treats files and directories uniformly ──────────
public class App {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory src = new Directory("src");
src.add(new File("Main.java", 12));
src.add(new File("Config.java", 5));
root.add(src);
root.add(new File("pom.xml", 3));
// Uniform operation on the whole tree
root.display("");
System.out.println("Total size: " + root.getSize() + "KB");
}
}File systems, org charts, category trees, or menus.
Calculating totals (sums, counts, prices) over a hierarchy.
Managing containers and widgets (DOM, Swing, Flutter).
When clients shouldn't care about the difference between a part and a whole.