DEV Community

Priyank Bhardwaj
Priyank Bhardwaj

Posted on

Composite Design Pattern — Simplify Tree Structures Like a Pro

Introduction

Have you ever worked with hierarchical structures like files and folders, or UI elements inside a layout?

If yes, you’ve already felt the need for the Composite Design Pattern — even if you didn’t know its name. The Composite Pattern lets you treat individual objects and compositions of objects uniformly.

In simple terms:
It allows clients to deal with a single object or a group of objects in the same way.

The core idea

          Component
         /         \
   Leaf              Composite
                      /     \
                 Leaf       Leaf
Enter fullscreen mode Exit fullscreen mode
  • Component → defines the common interface for all objects.
  • Leaf → represents the end objects (no children).
  • Composite → contains child components (can be both leaves and composites).

Real life Example: File System (Files and Folders)

Let’s implement a simple example in Java.

Step 1: Create the FileSystemComponent interface

interface FileSystemComponent {
    void showDetails();
}
Enter fullscreen mode Exit fullscreen mode

This acts as the common interface for both files and folders.

Step 2: Implement the File class (Leaf)

class File implements FileSystemComponent {
    private String name;

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

    @Override
    public void showDetails() {
        System.out.println("File: " + name);
    }
}
Enter fullscreen mode Exit fullscreen mode

This is our Leaf — will not have any further nodes (refer the UML diagram).

Step 3: Implement the Directory class (Composite)

import java.util.ArrayList;
import java.util.List;

class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> children = new ArrayList<>();

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

    public void addComponent(FileSystemComponent component) {
        children.add(component);
    }

    public void removeComponent(FileSystemComponent component) {
        children.remove(component);
    }

    @Override
    public void showDetails() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent component : children) {
            component.showDetails();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This is the Composite — it can hold both File and Directory objects.

Step 4: Demo — Build a File System Tree

public class CompositePatternDemo {
    public static void main(String[] args) {
        File file1 = new File("resume.pdf");
        File file2 = new File("photo.png");
        File file3 = new File("notes.txt");

        Directory documents = new Directory("Documents");
        Directory pictures = new Directory("Pictures");

        documents.addComponent(file1);
        pictures.addComponent(file2);
        pictures.addComponent(file3);

        Directory home = new Directory("Home");
        home.addComponent(documents);
        home.addComponent(pictures);

        home.showDetails();
    }
}
Enter fullscreen mode Exit fullscreen mode

Output

Directory: Home
Directory: Documents
File: resume.pdf
Directory: Pictures
File: photo.png
File: notes.txt
Enter fullscreen mode Exit fullscreen mode

How It Works

The magic here is polymorphism — both File and Directory implement FileSystemComponent.
This means the client (CompositePatternDemo) doesn’t care whether it’s working with a file or a folder — it just calls showDetails()!

Comparing this example to the component, leaf and composite analogy, here is summary

Role Class in Example Responsibility
Component FileSystemComponent Common interface
Leaf File Represents end object
Composite Directory Contains child components

Considerations

  • Composite can make the design too general, allowing you to add inappropriate objects.
  • Traversal or ordering logic can become complex if the hierarchy is deep.

When to Use It

  • You have objects organized in a tree structure (e.g., file systems, organizational charts, menus).
  • You want to perform operations on both single objects and groups in the same way.
  • You want to simplify client code that deals with complex hierarchies.

Key Benefits

  • Simplifies client code dealing with hierarchies
  • Encourages consistency across components
  • Makes it easy to add new types of components

This is Part 9 of the Java Design Patterns Series.

If you find it insightful, please share your feedback. Also let me know if you have used composite pattern in your projects.

Next Up: Bridge Design Patterns!

Top comments (0)