Overview
The Composite Pattern is a structural design pattern that enables you to compose objects into tree structures to represent part-whole hierarchies. This pattern allows clients to treat individual objects and compositions of objects uniformly.
It is commonly used when you have a tree-like structure, such as a file system, where some elements (files) are individual objects and others (directories) are containers of other objects.
Key Participants
Component
Defines the interface for all objects in the composition (both leaf and composite).
Declares common operations applicable to both simple and complex objects.
Leaf
Represents individual objects in the composition that have no children.
Implements the Component interface.
Composite
Represents complex objects that may have children (other components).
Implements methods to manage child components (e.g., add(), remove(), getChild()).
Implements the Component interface.
Client
Works with objects through the Component interface.
Does not need to differentiate between individual (leaf) or composite objects.
Key Methods and Their Purposes
add(Component c): Adds a child component to a composite object.
remove(Component c): Removes a child component from a composite object.
getChild(int index): Returns a specific child component of a composite object.
operation(): Performs an action. This could be implemented differently in Leaf and Composite.
Implementation in Code
Example: File System
// Component
interface FileSystemComponent {
void showDetails();
}
// Leaf
class File implements FileSystemComponent {
private String name;
public File(String name) {
this.name = name;
}
@Override
public void showDetails() {
System.out.println("File: " + name);
}
}
// Composite
class Directory implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
public void add(FileSystemComponent component) {
children.add(component);
}
public void remove(FileSystemComponent component) {
children.remove(component);
}
public FileSystemComponent getChild(int index) {
return children.get(index);
}
@Override
public void showDetails() {
System.out.println("Directory: " + name);
for (FileSystemComponent child : children) {
child.showDetails();
}
}
}
// Client
public class CompositePatternDemo {
public static void main(String[] args) {
File file1 = new File("File1.txt");
File file2 = new File("File2.txt");
Directory directory = new Directory("MyFolder");
directory.add(file1);
directory.add(file2);
directory.showDetails();
}
}
Advantages
Uniformity: Treats individual objects and composites the same way.
Extensibility: Easy to add new types of components without affecting the client.
Tree Structure Representation: Natural way to model hierarchical structures.
Disadvantages
Complexity: Managing the composition can become complex, especially when there are many levels.
Overhead: Can introduce additional overhead when managing child components in composites.
Real-World Examples
File System: Files and directories (folders) as individual and composite objects.
Graphic Editor: Shapes (circle, rectangle) as leaf objects, and groups of shapes as composite objects.
Menu System: Individual menu items as leaves, and menus as composites.
When to Use Composite Pattern?
When you want to represent part-whole hierarchies.
When you want the client to treat individual objects and composites uniformly.
When the objects need to be organized into a tree structure.
.