Composite Design Pattern in Java
Introduction
In many software systems, you need to represent part-whole hierarchies where clients treat individual objects and groups of objects uniformly. The Composite Pattern is a structural design pattern that allows this by composing objects into tree structures.
Think of it like a folder system on your computer: a folder can contain files or other folders, and the client can interact with both in the same way.
Definition
The Composite Pattern is a structural design pattern that lets you compose objects into tree structures and treat individual objects and compositions uniformly.
Key Idea: Use a common interface for both leaf objects (individual items) and composite objects (groups of items).
When to Use the Composite Pattern
- When you need to represent hierarchical tree structures.
- When clients should treat individual objects and groups uniformly.
- When adding new types of components should not require changing client code.
Real-World Example
- File System: Folders contain files or subfolders. Both support operations like display() or size().
- Company Hierarchy: Departments contain employees or sub-departments, all supporting getSalary() or printDetails().
Java Implementation
Let’s implement a file system using the Composite Pattern:
Step 1: Define the Component Interface
import java.util.List;
interface FileSystem {
void showDetails(); // Common operation for leaf and composite
}
Step 2: Create the Leaf Class
// Leaf class
class File implements FileSystem {
private String name;
public File(String name) {
this.name = name;
}
@Override
public void showDetails() {
System.out.println("File: " + name);
}
}
Step 3: Create the Composite Class
import java.util.ArrayList;
import java.util.List;
// Composite class
class Folder implements FileSystem {
private String name;
private List items = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public void add(FileSystem item) {
items.add(item);
}
public void remove(FileSystem item) {
items.remove(item);
}
@Override
public void showDetails() {
System.out.println("Folder: " + name);
for (FileSystem item : items) {
item.showDetails(); // Recursive call
}
}
}
Step 4: Test the Composite Pattern
public class Main {
public static void main(String[] args) {
File file1 = new File("Resume.docx");
File file2 = new File("Photo.png");
Folder folder1 = new Folder("My Documents");
folder1.add(file1);
folder1.add(file2);
File file3 = new File("Presentation.pptx");
Folder folder2 = new Folder("Work Documents");
folder2.add(file3);
folder2.add(folder1); // Nested folder
folder2.showDetails();
}
}
Output:
Folder: Work Documents
File: Presentation.pptx
Folder: My Documents
File: Resume.docx
File: Photo.png
Advantages of the Composite Pattern
- Simplifies Client Code: Clients interact with a single interface regardless of individual or composite objects.
- Hierarchy Representation: Ideal for tree-like structures.
- Flexibility: Easy to add new leaf or composite objects.
- Recursive Structure: Supports operations recursively over complex hierarchies.
Disadvantages
- Design Complexity: Can become complex for large systems with deep hierarchies.
- Type Safety: Clients may need to check object types if certain operations apply only to leaves.
- Overhead: Managing recursive calls and composite structures may incur performance overhead.
Summary
The Composite Pattern is a powerful structural pattern that treats individual objects and groups uniformly, making it ideal for hierarchical structures like file systems or organizational charts. By using a common interface, it simplifies client interactions and provides a flexible and scalable design.
Next, we will explore the Decorator Pattern, which allows dynamic addition of behavior to objects without modifying their structure.