Home/Learn/Low Level Design/Composite Pattern

Composite Pattern

Intermediate
Structural Patterns

Composes objects into tree structures to represent part-whole hierarchies, letting clients treat individual objects and compositions uniformly.

Overview

The Composite pattern models tree structures where leaf nodes and composite nodes are treated identically via a common Component interface. The key insight is that a Composite contains a list of Component children — each child can itself be a Composite or a Leaf. This enables recursive operations (compute total size, render all nodes) without the client needing to distinguish leaves from composites. Classic examples: file system (File is leaf, Directory is composite), UI component trees (Button is leaf, Panel is composite), arithmetic expression trees, org charts.

File System Example

FileSystemEntry is the Component interface. File is the Leaf — it has no children. Directory is the Composite — it holds a list of FileSystemEntry children and delegates size() to each recursively.

Java — Composite File System (File + Directory)
import java.util.ArrayList;
import java.util.List;

// Component interface
public interface FileSystemEntry {
    String getName();
    long size();           // recursive for directories
    void print(String indent);
}

// Leaf — has no children
public class File implements FileSystemEntry {
    private final String name;
    private final long sizeBytes;

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

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

    @Override
    public void print(String indent) {
        System.out.println(indent + "📄 " + name + " (" + sizeBytes + " bytes)");
    }
}

// Composite — contains children (Files or Directories)
public class Directory implements FileSystemEntry {
    private final String name;
    private final List<FileSystemEntry> children = new ArrayList<>();

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

    public void add(FileSystemEntry entry)    { children.add(entry); }
    public void remove(FileSystemEntry entry) { children.remove(entry); }

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

    @Override
    public long size() {
        return children.stream()
                       .mapToLong(FileSystemEntry::size) // recursive
                       .sum();
    }

    @Override
    public void print(String indent) {
        System.out.println(indent + "📁 " + name + "/ (" + size() + " bytes)");
        children.forEach(child -> child.print(indent + "  "));
    }
}

// Building the tree
Directory root = new Directory("root");

Directory src = new Directory("src");
src.add(new File("Main.java", 1024));
src.add(new File("Config.java", 512));

Directory resources = new Directory("resources");
resources.add(new File("application.yml", 256));

root.add(src);
root.add(resources);
root.add(new File("README.md", 128));

root.print("");
System.out.println("Total size: " + root.size() + " bytes"); // 1920

Expression Tree

Composite naturally models arithmetic expression trees. Number is a Leaf; Addition and Multiplication are Composites. evaluate() recursively computes the result.

Java — Composite Expression Tree
// Component
public interface Expression {
    int evaluate();
}

// Leaf
public class Number implements Expression {
    private final int value;
    public Number(int value) { this.value = value; }
    @Override public int evaluate() { return value; }
}

// Composite — binary operation
public class Addition implements Expression {
    private final Expression left, right;
    public Addition(Expression left, Expression right) {
        this.left = left; this.right = right;
    }
    @Override public int evaluate() { return left.evaluate() + right.evaluate(); }
}

public class Multiplication implements Expression {
    private final Expression left, right;
    public Multiplication(Expression left, Expression right) {
        this.left = left; this.right = right;
    }
    @Override public int evaluate() { return left.evaluate() * right.evaluate(); }
}

// (3 + 4) * (2 + 5) = 49
Expression expr = new Multiplication(
    new Addition(new Number(3), new Number(4)),
    new Addition(new Number(2), new Number(5)));
System.out.println(expr.evaluate()); // 49

Key Points to Remember

  • 1Composite lets clients treat Leaf and Composite nodes uniformly through the Component interface.
  • 2The Composite holds a list of Component children — each may be a Leaf or another Composite (recursion).
  • 3size(), evaluate(), render() methods are naturally recursive in Composite structures.
  • 4The Component interface should not expose child management (add/remove) — that belongs only on Composite.
  • 5Real-world: javax.swing.JComponent (UI tree), XML DOM, org charts, JSON/YAML object trees.

Interview Questions

Sign in to ask Aria
1

What problem does the Composite pattern solve?

EasyAmazon
2

Should add() and remove() be on the Component interface or only on Composite?

HardGoogle
3

How does Composite relate to the Visitor pattern?

HardMicrosoft
4

Implement a menu system (MenuItem and Menu) using Composite.

MediumFlipkart

Ask Aria about Composite Pattern

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…