Open In App

Composite Design Pattern in Java

Last Updated : 06 Sep, 2025
Comments
Improve
Suggest changes
22 Likes
Like
Report

The Composite Design Pattern is a structural pattern that organizes objects into tree structures, allowing clients to treat individual objects and groups of objects uniformly.

As described by the Gang of four, "Compose objects into tree structure to represent part-whole hierarchies. Composite lets client treat individual objects and compositions of objects uniformly".

composite
Composite Design

The key concept is that you can manipulate a single instance of the object just as you would manipulate a group of them. The operations you can perform on all the composite objects often have the least common denominator relationship.

Components of Composite Design Pattern

Let's understand this with the help of Diagram:

Components--Photoroompng-Photoroom

Here These are introductions for the Components of the Composite Design:

  • Component – Defines a common interface that both leaf objects and composite objects implement.
  • Leaf – Represents simple, indivisible objects that perform operations directly.
  • Composite – Represents complex objects that can contain children (leaves or other composites) and delegate operations to them.
  • Client – Works with all objects through the Component interface, treating individual and composite objects uniformly.

Composite Design Pattern example in Java

Imagine you are building a project management system where tasks can be either simple tasks or a collection of tasks (subtasks) forming a larger task.

composite-class-dig

1. Task (Component)

  • Represents the common interface for both simple tasks and task lists.
  • Defines methods such as getTitle(), setTitle(), and display().
C++
#include <string>
#include <iostream>

class Task {
public:
    virtual std::string getTitle() const = 0;
    virtual void setTitle(const std::string& title) = 0;
    virtual void display() const = 0;
};
Java
// Component

public interface Task {
    String getTitle();
    void setTitle(String title);
    void display();
}
Python
from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def get_title(self):
        pass

    @abstractmethod
    def set_title(self, title):
        pass

    @abstractmethod
    def display(self):
        pass
JavaScript
class Task {
    constructor() {
        if (new.target === Task) {
            throw new TypeError('Cannot construct Task instances directly');
        }
    }

    getTitle() {
        throw new Error('Method getTitle() must be implemented.');
    }

    setTitle(title) {
        throw new Error('Method setTitle() must be implemented.');
    }

    display() {
        throw new Error('Method display() must be implemented.');
    }
}


2. SimpleTask (Leaf)

  • Represents an individual task with a title.
  • Implements the Task interface.
C++
// Leaf

#include <iostream>
#include <string>
using namespace std;

class Task {
public:
    virtual string getTitle() = 0;
    virtual void setTitle(string title) = 0;
    virtual void display() = 0;
};

class SimpleTask : public Task {
private:
    string title;
public:
    SimpleTask(string title) : title(title) {}

    string getTitle() override {
        return title;
    }

    void setTitle(string title) override {
        this->title = title;
    }

    void display() override {
        cout << "Simple Task: " << title << endl;
    }
};
Java
// Leaf

public class SimpleTask implements Task {
    private String title;

    public SimpleTask(String title) {
        this.title = title;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public void display() {
        System.out.println("Simple Task: " + title);
    }
}
Python
# Leaf

class Task:
    def get_title(self):
        pass

    def set_title(self, title):
        pass

    def display(self):
        pass

class SimpleTask(Task):
    def __init__(self, title):
        self.title = title

    def get_title(self):
        return self.title

    def set_title(self, title):
        self.title = title

    def display(self):
        print(f'Simple Task: {self.title}')
JavaScript
// Leaf

class SimpleTask {
    constructor(title) {
        this.title = title;
    }

    getTitle() {
        return this.title;
    }

    setTitle(title) {
        this.title = title;
    }

    display() {
        console.log(`Simple Task: ${this.title}`);
    }
}


3. TaskList (Composite)

  • Represents a collection of tasks, which can include both simple tasks and other task lists.
  • Implements the Task interface but also has a list of tasks (List<Task>).
  • Defines methods to add, remove, and display tasks.
C++
#include <iostream>
#include <vector>
#include <string>

class Task {
public:
    virtual std::string getTitle() = 0;
    virtual void setTitle(std::string title) = 0;
    virtual void display() = 0;
};

class TaskList : public Task {
private:
    std::string title;
    std::vector<Task*> tasks;

public:
    TaskList(std::string title) : title(title) {}

    std::string getTitle() override {
        return title;
    }

    void setTitle(std::string title) override {
        this->title = title;
    }

    void addTask(Task* task) {
        tasks.push_back(task);
    }

    void removeTask(Task* task) {
        tasks.erase(std::remove(tasks.begin(), tasks.end(), task), tasks.end());
    }

    void display() override {
        std::cout << "Task List: " << title << std::endl;
        for (Task* task : tasks) {
            task->display();
        }
    }
};
Java
import java.util.ArrayList;
import java.util.List;

// Composite

public class TaskList implements Task {
    private String title;
    private List<Task> tasks;

    public TaskList(String title) {
        this.title = title;
        this.tasks = new ArrayList<>();
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    public void addTask(Task task) {
        tasks.add(task);
    }

    public void removeTask(Task task) {
        tasks.remove(task);
    }

    @Override
    public void display() {
        System.out.println("Task List: " + title);
        for (Task task : tasks) {
            task.display();
        }
    }
}
Python
class TaskList:
    def __init__(self, title):
        self.title = title
        self.tasks = []

    def get_title(self):
        return self.title

    def set_title(self, title):
        self.title = title

    def add_task(self, task):
        self.tasks.append(task)

    def remove_task(self, task):
        self.tasks.remove(task)

    def display(self):
        print(f'Task List: {self.title}')
        for task in self.tasks:
            task.display()
JavaScript
class TaskList {
    constructor(title) {
        this.title = title;
        this.tasks = [];
    }

    getTitle() {
        return this.title;
    }

    setTitle(title) {
        this.title = title;
    }

    addTask(task) {
        this.tasks.push(task);
    }

    removeTask(task) {
        this.tasks = this.tasks.filter(t => t !== task);
    }

    display() {
        console.log(`Task List: ${this.title}`);
        this.tasks.forEach(task => task.display());
    }
}



4. TaskManagementApp (Client)

  • Represents the application that uses the Composite Design Pattern to manage tasks.
  • It creates a mix of simple tasks and task lists, showcasing how the Composite pattern allows treating both individual tasks and task collections uniformly.
  • The created tasks are displayed in a hierarchical structure to illustrate the pattern's flexibility and uniform handling of different task types.
C++
#include <iostream>
#include <vector>
#include <string>

class Task {
public:
    virtual void display() const = 0;
};

class SimpleTask : public Task {
private:
    std::string description;
public:
    SimpleTask(const std::string& desc) : description(desc) {}
    void display() const override {
        std::cout << "- " << description << std::endl;
    }
};

class TaskList : public Task {
private:
    std::string name;
    std::vector<Task*> tasks;
public:
    TaskList(const std::string& n) : name(n) {}
    void addTask(Task* task) {
        tasks.push_back(task);
    }
    void display() const override {
        std::cout << name << std::endl;
        for (const auto& task : tasks) {
            task->display();
        }
    }
};

int main() {
    // Creating simple tasks
    Task* simpleTask1 = new SimpleTask("Complete Coding");
    Task* simpleTask2 = new SimpleTask("Write Documentation");

    // Creating a task list
    TaskList* projectTasks = new TaskList("Project Tasks");
    projectTasks->addTask(simpleTask1);
    projectTasks->addTask(simpleTask2);

    // Nested task list
    TaskList* phase1Tasks = new TaskList("Phase 1 Tasks");
    phase1Tasks->addTask(new SimpleTask("Design"));
    phase1Tasks->addTask(new SimpleTask("Implementation"));

    projectTasks->addTask(phase1Tasks);

    // Displaying tasks
    projectTasks->display();

    delete simpleTask1;
    delete simpleTask2;
    delete projectTasks;
    delete phase1Tasks;
    return 0;
}
Java
// Client

public class TaskManagementApp {
    public static void main(String[] args) {
        // Creating simple tasks
        Task simpleTask1 = new SimpleTask("Complete Coding");
        Task simpleTask2 = new SimpleTask("Write Documentation");

        // Creating a task list
        TaskList projectTasks = new TaskList("Project Tasks");
        projectTasks.addTask(simpleTask1);
        projectTasks.addTask(simpleTask2);

        // Nested task list
        TaskList phase1Tasks = new TaskList("Phase 1 Tasks");
        phase1Tasks.addTask(new SimpleTask("Design"));
        phase1Tasks.addTask(new SimpleTask("Implementation"));

        projectTasks.addTask(phase1Tasks);

        // Displaying tasks
        projectTasks.display();
    }
}
Python
from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def display(self):
        pass

class SimpleTask(Task):
    def __init__(self, description):
        self.description = description

    def display(self):
        print(f'- {self.description}')

class TaskList(Task):
    def __init__(self, name):
        self.name = name
        self.tasks = []

    def addTask(self, task):
        self.tasks.append(task)

    def display(self):
        print(self.name)
        for task in self.tasks:
            task.display()

# Client
def main():
    # Creating simple tasks
    simpleTask1 = SimpleTask('Complete Coding')
    simpleTask2 = SimpleTask('Write Documentation')

    # Creating a task list
    projectTasks = TaskList('Project Tasks')
    projectTasks.addTask(simpleTask1)
    projectTasks.addTask(simpleTask2)

    # Nested task list
    phase1Tasks = TaskList('Phase 1 Tasks')
    phase1Tasks.addTask(SimpleTask('Design'))
    phase1Tasks.addTask(SimpleTask('Implementation'))

    projectTasks.addTask(phase1Tasks)

    # Displaying tasks
    projectTasks.display()

if __name__ == '__main__':
    main()
JavaScript
class Task {
    display() {
        throw new Error('Method not implemented.');
    }
}

class SimpleTask extends Task {
    constructor(description) {
        super();
        this.description = description;
    }

    display() {
        console.log(`- ${this.description}`);
    }
}

class TaskList extends Task {
    constructor(name) {
        super();
        this.name = name;
        this.tasks = [];
    }

    addTask(task) {
        this.tasks.push(task);
    }

    display() {
        console.log(this.name);
        this.tasks.forEach(task => task.display());
    }
}

// Client
const main = () => {
    // Creating simple tasks
    const simpleTask1 = new SimpleTask('Complete Coding');
    const simpleTask2 = new SimpleTask('Write Documentation');

    // Creating a task list
    const projectTasks = new TaskList('Project Tasks');
    projectTasks.addTask(simpleTask1);
    projectTasks.addTask(simpleTask2);

    // Nested task list
    const phase1Tasks = new TaskList('Phase 1 Tasks');
    phase1Tasks.addTask(new SimpleTask('Design'));
    phase1Tasks.addTask(new SimpleTask('Implementation'));

    projectTasks.addTask(phase1Tasks);

    // Displaying tasks
    projectTasks.display();
};

main();

Complete code for the above example:

This code includes the Task, SimpleTask, TaskList, and TaskManagementApp classes. It demonstrates the Composite Design Pattern for organizing tasks in a project management system.

C++
#include <iostream>
#include <vector>
#include <string>

// Component
class Task {
public:
    virtual std::string getTitle() const = 0;
    virtual void setTitle(const std::string& title) = 0;
    virtual void display() const = 0;
};

// Leaf
class SimpleTask : public Task {
private:
    std::string title;
public:
    SimpleTask(const std::string& title) : title(title) {}
    std::string getTitle() const override { return title; }
    void setTitle(const std::string& title) override { this->title = title; }
    void display() const override { std::cout << "Simple Task: " << title << std::endl; }
};

// Composite
class TaskList : public Task {
private:
    std::string title;
    std::vector<Task*> tasks;
public:
    TaskList(const std::string& title) : title(title) {}
    std::string getTitle() const override { return title; }
    void setTitle(const std::string& title) override { this->title = title; }
    void addTask(Task* task) { tasks.push_back(task); }
    void removeTask(Task* task) { tasks.erase(std::remove(tasks.begin(), tasks.end(), task), tasks.end()); }
    void display() const override {
        std::cout << "Task List: " << title << std::endl;
        for (const auto& task : tasks) {
            task->display();
        }
    }
};

// Client
int main() {
    // Creating simple tasks
    Task* simpleTask1 = new SimpleTask("Complete Coding");
    Task* simpleTask2 = new SimpleTask("Write Documentation");

    // Creating a task list
    Task* projectTasks = new TaskList("Project Tasks");
    projectTasks->addTask(simpleTask1);
    projectTasks->addTask(simpleTask2);

    // Nested task list
    Task* phase1Tasks = new TaskList("Phase 1 Tasks");
    phase1Tasks->addTask(new SimpleTask("Design"));
    phase1Tasks->addTask(new SimpleTask("Implementation"));

    projectTasks->addTask(phase1Tasks);

    // Displaying tasks
    projectTasks->display();

    // Clean up
    delete simpleTask1;
    delete simpleTask2;
    delete phase1Tasks;
    delete projectTasks;
    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;

// Component
interface Task {
    String getTitle();
    void setTitle(String title);
    void display();
}

// Leaf
class SimpleTask implements Task {
    private String title;

    public SimpleTask(String title) {
        this.title = title;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public void display() {
        System.out.println("Simple Task: " + title);
    }
}

// Composite
class TaskList implements Task {
    private String title;
    private List<Task> tasks;

    public TaskList(String title) {
        this.title = title;
        this.tasks = new ArrayList<>();
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public void setTitle(String title) {
        this.title = title;
    }

    public void addTask(Task task) {
        tasks.add(task);
    }

    public void removeTask(Task task) {
        tasks.remove(task);
    }

    @Override
    public void display() {
        System.out.println("Task List: " + title);
        for (Task task : tasks) {
            task.display();
        }
    }
}

// Client
public class TaskManagementApp {
    public static void main(String[] args) {
        // Creating simple tasks
        Task simpleTask1 = new SimpleTask("Complete Coding");
        Task simpleTask2 = new SimpleTask("Write Documentation");

        // Creating a task list
        TaskList projectTasks = new TaskList("Project Tasks");
        projectTasks.addTask(simpleTask1);
        projectTasks.addTask(simpleTask2);

        // Nested task list
        TaskList phase1Tasks = new TaskList("Phase 1 Tasks");
        phase1Tasks.addTask(new SimpleTask("Design"));
        phase1Tasks.addTask(new SimpleTask("Implementation"));

        projectTasks.addTask(phase1Tasks);

        // Displaying tasks
        projectTasks.display();
    }
}
Python
# Component
from abc import ABC, abstractmethod

class Task(ABC):
    @abstractmethod
    def get_title(self):
        pass

    @abstractmethod
    def set_title(self, title):
        pass

    @abstractmethod
    def display(self):
        pass

# Leaf
class SimpleTask(Task):
    def __init__(self, title):
        self._title = title

    def get_title(self):
        return self._title

    def set_title(self, title):
        self._title = title

    def display(self):
        print(f'Simple Task: {self._title}')

# Composite
class TaskList(Task):
    def __init__(self, title):
        self._title = title
        self._tasks = []

    def get_title(self):
        return self._title

    def set_title(self, title):
        self._title = title

    def add_task(self, task):
        self._tasks.append(task)

    def remove_task(self, task):
        self._tasks.remove(task)

    def display(self):
        print(f'Task List: {self._title}')
        for task in self._tasks:
            task.display()

# Client
if __name__ == '__main__':
    # Creating simple tasks
    simple_task1 = SimpleTask('Complete Coding')
    simple_task2 = SimpleTask('Write Documentation')

    # Creating a task list
    project_tasks = TaskList('Project Tasks')
    project_tasks.add_task(simple_task1)
    project_tasks.add_task(simple_task2)

    # Nested task list
    phase1_tasks = TaskList('Phase 1 Tasks')
    phase1_tasks.add_task(SimpleTask('Design'))
    phase1_tasks.add_task(SimpleTask('Implementation'))

    project_tasks.add_task(phase1_tasks)

    # Displaying tasks
    project_tasks.display()
JavaScript
// Component
class Task {
    constructor(title) {
        if (this.constructor === Task) {
            throw new TypeError('Abstract class "Task" cannot be instantiated directly.');
        }
        this._title = title;
    }

    getTitle() {
        return this._title;
    }

    setTitle(title) {
        this._title = title;
    }

    display() {
        throw new TypeError('Method "display" must be implemented.');
    }
}

// Leaf
class SimpleTask extends Task {
    constructor(title) {
        super(title);
    }

    display() {
        console.log(`Simple Task: ${this._title}`);
    }
}

// Composite
class TaskList extends Task {
    constructor(title) {
        super(title);
        this._tasks = [];
    }

    addTask(task) {
        this._tasks.push(task);
    }

    removeTask(task) {
        this._tasks = this._tasks.filter(t => t !== task);
    }

    display() {
        console.log(`Task List: ${this._title}`);
        this._tasks.forEach(task => task.display());
    }
}

// Client
(() => {
    // Creating simple tasks
    const simpleTask1 = new SimpleTask('Complete Coding');
    const simpleTask2 = new SimpleTask('Write Documentation');

    // Creating a task list
    const projectTasks = new TaskList('Project Tasks');
    projectTasks.addTask(simpleTask1);
    projectTasks.addTask(simpleTask2);

    // Nested task list
    const phase1Tasks = new TaskList('Phase 1 Tasks');
    phase1Tasks.addTask(new SimpleTask('Design'));
    phase1Tasks.addTask(new SimpleTask('Implementation'));

    projectTasks.addTask(phase1Tasks);

    // Displaying tasks
    projectTasks.display();
})();



Output
Task List: Project Tasks
Simple Task: Complete Coding
Simple Task: Write Documentation
Task List: Phase 1 Tasks
Simple Task: Design
Simple Task: Implementation

Why do we need Composite Design Pattern?

The Composite Design Pattern was created to address specific challenges related to the representation and manipulation of hierarchical structures in a uniform way.

Here are some points that highlight the need for the Composite Design Pattern:

  1. Uniform Interface – Treats individual and composite objects the same, simplifying client code.
  2. Hierarchical Structures – Ideal for tree-like part-whole relationships.
  3. Flexibility & Scalability – Allows dynamic composition, making structures easy to extend or modify.
  4. Common Operations – Defines shared operations at the component level, reducing duplication.
  5. Client Simplification – Provides a unified way to handle complex structures efficiently.

Use of Composite Design Pattern

The Composite Pattern allows uniform treatment of single components and groups, simplifying design and improving efficiency.

  • Enables clients to work with individual components and collections in the same way.
  • Reduces duplicate logic in handling primitives vs composites.
  • Lowers resource usage by minimizing unnecessary objects.
  • Supports resource sharing for better scalability and performance.

When not to use Composite Design Pattern?

Composite Design Pattern makes it harder to restrict the type of components of a composite. So it should not be used when you don't want to represent a full or partial hierarchy of objects.

  • Composite Design Pattern can make the design overly general.
  • It makes harder to restrict the components of a composite.
  • Sometimes you want a composite to have only certain components. With Composite, you can't rely on the type system to enforce those constraints for you.
  • Instead you'll have to use run-time checks.



Article Tags :

Explore