Introduction
Have you ever worked with hierarchical structures like files and folders, or UI elements inside a layout?
If yes, you’ve already felt the need for the Composite Design Pattern — even if you didn’t know its name. The Composite Pattern lets you treat individual objects and compositions of objects uniformly.
In simple terms:
It allows clients to deal with a single object or a group of objects in the same way.
The core idea
Component / \ Leaf Composite / \ Leaf Leaf - Component → defines the common interface for all objects.
- Leaf → represents the end objects (no children).
- Composite → contains child components (can be both leaves and composites).
Real life Example: File System (Files and Folders)
Let’s implement a simple example in Java.
Step 1: Create the FileSystemComponent interface
interface FileSystemComponent { void showDetails(); } This acts as the common interface for both files and folders.
Step 2: Implement the File class (Leaf)
class File implements FileSystemComponent { private String name; public File(String name) { this.name = name; } @Override public void showDetails() { System.out.println("File: " + name); } } This is our Leaf — will not have any further nodes (refer the UML diagram).
Step 3: Implement the Directory class (Composite)
import java.util.ArrayList; import java.util.List; class Directory implements FileSystemComponent { private String name; private List<FileSystemComponent> children = new ArrayList<>(); public Directory(String name) { this.name = name; } public void addComponent(FileSystemComponent component) { children.add(component); } public void removeComponent(FileSystemComponent component) { children.remove(component); } @Override public void showDetails() { System.out.println("Directory: " + name); for (FileSystemComponent component : children) { component.showDetails(); } } } This is the Composite — it can hold both File and Directory objects.
Step 4: Demo — Build a File System Tree
public class CompositePatternDemo { public static void main(String[] args) { File file1 = new File("resume.pdf"); File file2 = new File("photo.png"); File file3 = new File("notes.txt"); Directory documents = new Directory("Documents"); Directory pictures = new Directory("Pictures"); documents.addComponent(file1); pictures.addComponent(file2); pictures.addComponent(file3); Directory home = new Directory("Home"); home.addComponent(documents); home.addComponent(pictures); home.showDetails(); } } Output
Directory: Home Directory: Documents File: resume.pdf Directory: Pictures File: photo.png File: notes.txt How It Works
The magic here is polymorphism — both File and Directory implement FileSystemComponent.
This means the client (CompositePatternDemo) doesn’t care whether it’s working with a file or a folder — it just calls showDetails()!
Comparing this example to the component, leaf and composite analogy, here is summary
| Role | Class in Example | Responsibility |
|---|---|---|
| Component | FileSystemComponent | Common interface |
| Leaf | File | Represents end object |
| Composite | Directory | Contains child components |
Considerations
- Composite can make the design too general, allowing you to add inappropriate objects.
- Traversal or ordering logic can become complex if the hierarchy is deep.
When to Use It
- You have objects organized in a tree structure (e.g., file systems, organizational charts, menus).
- You want to perform operations on both single objects and groups in the same way.
- You want to simplify client code that deals with complex hierarchies.
Key Benefits
- Simplifies client code dealing with hierarchies
- Encourages consistency across components
- Makes it easy to add new types of components
This is Part 9 of the Java Design Patterns Series.
If you find it insightful, please share your feedback. Also let me know if you have used composite pattern in your projects.
Next Up: Bridge Design Patterns!
Top comments (0)