Composite Design Pattern

The Composite Design Pattern is a structural pattern that allows treating individual objects and compositions of objects uniformly. It is useful when you need to represent a hierarchy of objects, such as a tree structure.
Components of the Composite Design Pattern
The Composite Design Pattern consists of the following key components:
Component (Abstract Interface)
Defines a common interface for both individual objects (leaf) and composite objects.
Declares methods for accessing and managing child components.
Example:
FileSystem
interface in the file system example.
package DesignPatterns.structural.composite.filesysteminterface;
public interface FileSystem {
void ls();
}
Leaf (Individual Object)
Represents a single object in the composition.
Implements the
Component
interface but does not contain child objects.Example:
File
class in the file system.
package DesignPatterns.structural.composite.filesysteminterface;
public class File implements FileSystem {
private String fileName;
public File(String fileName) {
this.fileName = fileName;
}
@Override
public void ls() {
System.out.println("File: " + fileName);
}
}
Composite (Container for Other Components)
Contains multiple child components (both
Leaf
and otherComposite
objects).Implements the
Component
interface and provides methods to add, remove, and access children.Example:
Directory
class in the file system.
package DesignPatterns.structural.composite.filesysteminterface;
import java.util.ArrayList;
import java.util.List;
public class Directory implements FileSystem {
private String directoryName;
private List<FileSystem> fileSystemList;
public Directory(String directoryName) {
this.directoryName = directoryName;
this.fileSystemList = new ArrayList<>();
}
// Add component method
public void addComponent(FileSystem fs) {
fileSystemList.add(fs);
}
@Override
public void ls() {
System.out.println("Directory: " + directoryName);
for (FileSystem fileSystem : fileSystemList) {
fileSystem.ls();
}
}
}
Client
Uses the Component interface to interact with both Leaf and Composite objects.
Treats both individual objects and groups the same way.
Example:
Client
class.
package DesignPatterns.structural.composite.filesysteminterface;
public class Client {
public static void main(String[] args) {
Directory root = new Directory("DSA");
File file1 = new File("code1.cpp");
File file2 = new File("code2.cpp");
root.addComponent(file1);
root.addComponent(file2);
Directory subDir = new Directory("DAYS");
File file3 = new File("day1code.cpp");
File file4 = new File("day2code.cpp");
subDir.addComponent(file3);
subDir.addComponent(file4);
root.addComponent(subDir);
// Print the entire directory structure
root.ls();
}
}
Pros and Cons of the Composite Design Pattern
Pros (Advantages)
Simplifies Complex Hierarchies
Allows handling individual (
Leaf
) and grouped (Composite
) objects uniformly.Reduces code duplication when working with tree structures.
Supports Open/Closed Principle
- You can add new components (like
VideoFile
,AudioFile
,ImageFile
) without modifying existing code.
- You can add new components (like
Encapsulation and Loose Coupling
- The
Client
does not need to worry whether it is dealing with aLeaf
orComposite
object.
- The
Scalability and Flexibility
You can dynamically add or remove components at runtime.
Works well for recursive structures like file systems, organizations, and UI components.
Uniform Operations on Hierarchical Data
- Operations like traversing, rendering, or searching can be performed easily on the entire tree.
Cons (Disadvantages)
Increases Complexity
- Overhead of managing parent-child relationships, especially if you only need a simple structure.
Difficult to Restrict Certain Behaviors
Example:
A
File
should not contain child elements.But since
File
andDirectory
share the same interface (FileSystem
), you may need extra validation.
Potential Performance Issues
- Deep trees with many recursive operations (
ls()
) may impact performance.
- Deep trees with many recursive operations (
Complicates Debugging and Maintenance
- When working with deeply nested trees, debugging and tracing function calls can be harder.
When to Use:
- Great for modeling tree structures (file systems, UI components, organizations).
Subscribe to my newsletter
Read articles from Nivedita Kumari directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Nivedita Kumari
Nivedita Kumari
I am wokring as SDE-1 at BetterPlace