The Composite Design Pattern is a structural pattern that allows you to compose objects into tree-like structures to represent part-whole hierarchies. This pattern treats both individual objects and compositions of objects uniformly. It’s useful when you need to work with hierarchies of objects and want to treat single objects and compositions of objects in a consistent way.
Components of the Composite Design Pattern:
- Component: The abstract base class or interface that declares the common interface for both leaf and composite objects.
- Leaf: The concrete class that represents individual objects in the composition. It implements the component interface.
- Composite: The concrete class that represents compositions of leaf objects. It implements the component interface and contains child components.
Simple Example:
Let’s use a file system example where we have File
and Directory
components. A Directory
can contain multiple File
or Directory
objects, and we want to be able to treat both File
and Directory
objects uniformly.
1. Component Interface:
// Component interface
interface FileSystemComponent {
void showDetails();
}
2. Leaf:
// Leaf class
class File implements FileSystemComponent {
private String name;
public File(String name) {
this.name = name;
}
@Override
public void showDetails() {
System.out.println("File: " + name);
}
}
3. Composite:
import java.util.ArrayList;
import java.util.List;
// Composite class
class Directory implements FileSystemComponent {
private String name;
private List<FileSystemComponent> components = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
public void addComponent(FileSystemComponent component) {
components.add(component);
}
public void removeComponent(FileSystemComponent component) {
components.remove(component);
}
@Override
public void showDetails() {
System.out.println("Directory: " + name);
for (FileSystemComponent component : components) {
component.showDetails();
}
}
}
4. Client Code:
public class CompositePatternDemo {
public static void main(String[] args) {
// Create files
File file1 = new File("File1.txt");
File file2 = new File("File2.txt");
// Create directories
Directory directory1 = new Directory("Directory1");
Directory directory2 = new Directory("Directory2");
// Add files to directories
directory1.addComponent(file1);
directory2.addComponent(file2);
// Create a main directory and add subdirectories to it
Directory mainDirectory = new Directory("MainDirectory");
mainDirectory.addComponent(directory1);
mainDirectory.addComponent(directory2);
// Show details of all components
mainDirectory.showDetails();
}
}
Explanation:
- Component Interface:
FileSystemComponent
is the common interface for bothFile
andDirectory
. - Leaf:
File
is a concrete class that represents an individual file and implementsFileSystemComponent
. - Composite:
Directory
is a concrete class that represents a directory, which can contain otherFileSystemComponent
objects (eitherFile
or otherDirectory
objects). - Client Code: Demonstrates how to use the
Directory
andFile
classes to create a hierarchical structure and display details of all components.
Benefits of the Composite Design Pattern:
- Uniformity: Treats both individual objects and compositions of objects uniformly, simplifying client code.
- Flexibility: Allows clients to work with complex tree structures without needing to know the specifics of individual objects or compositions.
- Scalability: Makes it easy to add new leaf or composite types without modifying existing code.
Use Cases of the Composite Design Pattern:
- File Systems: Where files and directories need to be treated uniformly.
- Graphics Systems: Where shapes (like lines and circles) are composed into complex drawings.
- Menus and UI Components: Where individual items and menus (containing other items or menus) need to be managed uniformly.
The Composite Pattern is particularly useful when dealing with tree structures and hierarchies where operations need to be applied uniformly across both individual and composite elements.