Composite Pattern

The Composite Pattern is a structural design pattern used to compose objects into tree-like structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions of objects uniformly.

The Composite Pattern is a structural design pattern that allows you to compose objects into tree-like structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly, enabling recursive processing of complex structures as if they were single objects.

Important Components

  1. Component: An abstract class or interface defining common operations for both leaf and composite objects.
  2. Leaf: A basic element of the hierarchy that has no children and implements the Component interface.
  3. Composite: A container that holds a collection of Components (Leaves or other Composites) and implements the Component interface, often delegating operations to its children.
  4. Client: Interacts with the Component interface, unaware of whether it’s dealing with a Leaf or a Composite.

How It Works

  • The Component interface declares methods for operations (e.g., display, add, remove).
  • Leaf objects implement these methods for individual elements.
  • Composite objects implement the same methods, typically by iterating over their children and delegating the operation.
  • The client works with the Component interface, allowing uniform treatment of leaves and composites, often recursively traversing the hierarchy.

Sample Implementation

// Component Interface
interface FileSystemEntry {
    void display(int indent);
    String getName();
}

// Leaf
class File implements FileSystemEntry {
    private String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public void display(int indent) {
        System.out.println("  ".repeat(indent) + "- File: " + name);
    }

    @Override
    public String getName() {
        return name;
    }
}

// Composite
class Directory implements FileSystemEntry {
    private String name;
    private List<FileSystemEntry> entries;

    public Directory(String name) {
        this.name = name;
        this.entries = new ArrayList<>();
    }

    public void addEntry(FileSystemEntry entry) {
        entries.add(entry);
    }

    public void removeEntry(FileSystemEntry entry) {
        entries.remove(entry);
    }

    @Override
    public void display(int indent) {
        System.out.println("  ".repeat(indent) + "+ Directory: " + name);
        for (FileSystemEntry entry : entries) {
            entry.display(indent + 1);
        }
    }

    @Override
    public String getName() {
        return name;
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        // Create files (Leaves)
        FileSystemEntry file1 = new File("document.txt");
        FileSystemEntry file2 = new File("image.png");
        FileSystemEntry file3 = new File("data.csv");

        // Create directories (Composites)
        Directory root = new Directory("root");
        Directory home = new Directory("home");
        Directory user = new Directory("user");

        // Build hierarchy
        root.addEntry(home);
        home.addEntry(user);
        home.addEntry(file1);
        user.addEntry(file2);
        user.addEntry(file3);

        // Display the hierarchy
        root.display(0);
    }
}Code language: PHP (php)

Use Case and Implementation:

Representing and managing hierarchical structures like customer portfolios containing multiple accounts and investments.

//CompositePatternDemo.java
import java.util.ArrayList;
import java.util.List;
// Component interface
interface PortfolioComponent {
    double getValue();  // Method to get total value of the portfolio
}
// Leaf class representing an investment
class Investment implements PortfolioComponent {
    private String name;
    private double value;
    public Investment(String name, double value) {
        this.name = name;
        this.value = value;
    }
    @Override
    public double getValue() {
        return value;
    }
    public String getName() {
        return name;
    }
}
// Composite class representing a portfolio containing investments and sub-portfolios
class Portfolio implements PortfolioComponent {
    private String name;
    private List<PortfolioComponent> components;
    public Portfolio(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }
    public void addComponent(PortfolioComponent component) {
        components.add(component);
    }
    public void removeComponent(PortfolioComponent component) {
        components.remove(component);
    }
    @Override
    public double getValue() {
        double totalValue = 0.0;
        for (PortfolioComponent component : components) {
            totalValue += component.getValue();
        }
        return totalValue;
    }
}
public class CompositePatternDemo {
    public static void main(String[] args) {
        // Create leaf investments
        Investment stock1 = new Investment("Tech Stock", 5000);
        Investment stock2 = new Investment("Pharma Stock", 3000);
        // Create composite portfolio
        Portfolio portfolio = new Portfolio("Customer Portfolio");
        // Add investments to the portfolio
        portfolio.addComponent(stock1);
        portfolio.addComponent(stock2);
        // Create another sub-portfolio
        Portfolio subPortfolio = new Portfolio("Sub-Portfolio");
        // Add investments to the sub-portfolio
        Investment bond1 = new Investment("Corporate Bond", 2000);
        Investment fund1 = new Investment("Mutual Fund", 4000);
        subPortfolio.addComponent(bond1);
        subPortfolio.addComponent(fund1);
        // Add sub-portfolio to the main portfolio
        portfolio.addComponent(subPortfolio);
        // Calculate and print total value of the main portfolio
        double totalValue = portfolio.getValue();
        System.out.println("Total value of the portfolio: $" + totalValue);
    }
}

/*
C:\>javac CompositePatternDemo.java

C:\>java CompositePatternDemo
Total value of the portfolio: $14000.0
*/

Pros

  • Uniformity: Clients can treat individual objects and compositions identically.
  • Flexibility: Easily add new components (leaves or composites) to the hierarchy.
  • Recursive Processing: Simplifies operations like traversal or calculations over the entire structure.
  • Scalability: Supports complex tree-like structures without changing client code.

Cons

  • Complexity: Can make the design overly general, as all components must conform to the same interface.
  • Type Safety: May require type checking or casting to distinguish between leaves and composites.
  • Performance: Recursive operations on large hierarchies can be costly.

When to Use

  • When you need to represent part-whole hierarchies (e.g., file systems, organizational structures, UI components).
  • When clients should treat individual objects and compositions uniformly.
  • When you want to perform recursive operations on a tree-like structure.

Real-World Example

  • File Systems: Files (leaves) and directories (composites) are treated uniformly for operations like displaying or calculating size.
  • UI Frameworks: Widgets (leaves) and containers (composites) in a GUI are managed as a hierarchy (e.g., HTML DOM, Java Swing).
  • Organizational Structures: Employees (leaves) and departments (composites) in a company hierarchy.

The Composite Pattern is a structural design pattern that lets you treat individual objects and compositions of objects uniformly. In Java, it’s particularly useful for building tree-like structures such as file systems, GUI components, and organization hierarchies, where part-whole relationships need to be managed recursively. By defining a common interface for both simple and complex objects, the Composite Pattern simplifies client-side operations. It promotes flexibility, extensibility, and elegant recursive behavior, making your code easier to maintain and scale.

Scroll to Top