The Composite Pattern
The Composite Pattern
The Composite Pattern lets you treat individual objects and groups of objects the same way. Instead of writing separate code for a single item and a collection, you build a tree structure where every node - whether a leaf or a branch - shares the same interface. This is one of the most practical patterns you will encounter.
Why Composite Matters
Consider a file system. A file and a folder both have a name and a size. A folder contains other files and folders. Without Composite you would need different code paths for files versus directories. With Composite both implement the same FileItem interface, so calling getSize() works uniformly whether the item is a single file or a deeply nested folder.
Class Diagram
+-------------------+
| FileItem | <<interface>>
+-------------------+
| + getName() |
| + getSize() |
+--------+----------+
|
+------+------+-----------------+
| | |
+-----+------+ +---+------------+ +--+-----------+
| File | | Directory | | Link |
+------------+ +----------------+ +--------------+
| - name | | - children[] | | - name |
| - size | | + add(item) | | - targetSize |
+------------+ | + remove(item) | +--------------+
+----------------+
Code Example
First define the component interface that both files and directories share:
public interface FileItem {
String getName();
int getSize();
}
Now implement a leaf (File) and a composite (Directory):
public class File implements FileItem {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() { return name; }
public int getSize() { return size; }
}
public class Directory implements FileItem {
private String name;
private List<FileItem> children = new ArrayList<>();
public Directory(String name) { this.name = name; }
public String getName() { return name; }
public void add(FileItem item) { children.add(item); }
public int getSize() {
int total = 0;
for (FileItem item : children) {
total += item.getSize();
}
return total;
}
}
Usage is seamless - the client does not need to know if it is working with a file or a directory:
Directory root = new Directory("root");
File readme = new File("readme.txt", 200);
Directory src = new Directory("src");
src.add(new File("Main.java", 500));
root.add(readme);
root.add(src);
System.out.println("Total size: " + root.getSize());
Real-World Use
GUI toolkits use Composite for widgets. A Panel holds child components and each child - whether a button, label, or another panel - responds to paint() the same way. Menu systems also benefit: a menu item and a submenu both implement a common interface, so the menu bar can render and handle events uniformly.
Key Points
- Composite lets you build tree structures where leaves and branches share one interface.
- Operations on the composite automatically recurse into children.
- Clients do not need to distinguish between individual and composite objects.
- Adding new component types requires no changes to existing client code.
- Be careful with performance on very deep or wide trees.