Menu
Coddy logo textTech

Composite Pattern

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 101 of 104.

The Composite pattern lets you treat individual objects and groups of objects uniformly. It composes objects into tree structures where both single elements and containers of elements share the same interface. This is ideal for representing hierarchies like file systems, organization charts, or UI components.

The pattern has three key parts: a Component interface that defines common operations, Leaf classes representing individual objects, and Composite classes that contain children and delegate operations to them:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// Component interface
class FileSystemItem {
public:
    virtual void display(int indent = 0) const = 0;
    virtual int getSize() const = 0;
    virtual ~FileSystemItem() = default;
};

// Leaf - represents individual files
class File : public FileSystemItem {
    std::string name;
    int size;
public:
    File(const std::string& n, int s) : name(n), size(s) {}
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << name 
                  << " (" << size << " KB)\n";
    }
    int getSize() const override { return size; }
};

// Composite - contains other components
class Folder : public FileSystemItem {
    std::string name;
    std::vector<std::shared_ptr<FileSystemItem>> children;
public:
    Folder(const std::string& n) : name(n) {}
    
    void add(std::shared_ptr<FileSystemItem> item) {
        children.push_back(item);
    }
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << "[" << name << "]\n";
        for (const auto& child : children) {
            child->display(indent + 2);
        }
    }
    
    int getSize() const override {
        int total = 0;
        for (const auto& child : children) {
            total += child->getSize();
        }
        return total;
    }
};

The Folder composite stores children and implements operations by iterating through them. When you call getSize() on a folder, it recursively calculates the total size of all contained items. The client code doesn't need to know whether it's working with a file or a folder - both respond to the same interface.

Use Composite when you need to represent part-whole hierarchies and want clients to treat individual objects and compositions uniformly.

challenge icon

Challenge

Easy

Let's build an Organization Chart system using the Composite pattern. You'll create a hierarchy where both individual employees and departments (which contain other employees or sub-departments) can be treated uniformly. This mirrors how real companies are structured—departments contain people and other departments, forming a tree structure.

You'll organize your code across three files:

  • OrgComponent.h: Define the component interface that both employees and departments will implement.

    Create an abstract OrgComponent class with:

    • getName() — returns the component's name
    • getSalary() — returns the total salary (for employees, their own salary; for departments, the sum of all contained salaries)
    • display(int indent = 0) — displays the component with proper indentation

    Include a virtual destructor.

  • Organization.h: Implement the leaf and composite classes.

    Create an Employee class (the leaf) that stores a name and salary. Its display() method should print the employee's information in this format:

    [indent spaces]- [name] ($[salary])

    Create a Department class (the composite) that stores a name and a collection of OrgComponent children using std::shared_ptr. Implement:

    • add(std::shared_ptr<OrgComponent> component) — adds a child to the department
    • getSalary() — recursively calculates the total salary of all members
    • display() — prints the department name in brackets, then displays all children with increased indentation (add 2 spaces per level)

    The department's display format should be:

    [indent spaces][Department Name]
      [children displayed with indent + 2]
  • main.cpp: Build and display an organization structure.

    Read four inputs:

    1. Company name (string)
    2. Department name (string)
    3. First employee name and salary (format: name,salary)
    4. Second employee name and salary (format: name,salary)

    Build this structure: Create a company (top-level department), add a sub-department to it, and add both employees to that sub-department. Then display the entire organization and print the total company salary.

    After displaying the structure, print:

    Total Salary: $[amount]

For example, with inputs TechCorp, Engineering, Alice,75000, and Bob,65000:

[TechCorp]
  [Engineering]
    - Alice ($75000)
    - Bob ($65000)
Total Salary: $140000

With inputs StartupInc, Development, Carol,80000, and Dave,70000:

[StartupInc]
  [Development]
    - Carol ($80000)
    - Dave ($70000)
Total Salary: $150000

Notice how getSalary() works uniformly whether called on an employee or a department—the department automatically aggregates salaries from all its members. The client code doesn't need to distinguish between individual employees and entire departments when calculating totals or displaying the hierarchy.

Cheat sheet

The Composite pattern treats individual objects and groups of objects uniformly by composing them into tree structures where both single elements and containers share the same interface.

The pattern has three key parts:

  • Component: An interface defining common operations
  • Leaf: Classes representing individual objects
  • Composite: Classes that contain children and delegate operations to them

Example implementation of a file system:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// Component interface
class FileSystemItem {
public:
    virtual void display(int indent = 0) const = 0;
    virtual int getSize() const = 0;
    virtual ~FileSystemItem() = default;
};

// Leaf - represents individual files
class File : public FileSystemItem {
    std::string name;
    int size;
public:
    File(const std::string& n, int s) : name(n), size(s) {}
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << name 
                  << " (" << size << " KB)\n";
    }
    int getSize() const override { return size; }
};

// Composite - contains other components
class Folder : public FileSystemItem {
    std::string name;
    std::vector<std::shared_ptr<FileSystemItem>> children;
public:
    Folder(const std::string& n) : name(n) {}
    
    void add(std::shared_ptr<FileSystemItem> item) {
        children.push_back(item);
    }
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << "[" << name << "]\n";
        for (const auto& child : children) {
            child->display(indent + 2);
        }
    }
    
    int getSize() const override {
        int total = 0;
        for (const auto& child : children) {
            total += child->getSize();
        }
        return total;
    }
};

The composite stores children using std::shared_ptr and implements operations by iterating through them. Operations like getSize() work recursively, calculating totals across the entire hierarchy.

Use Composite when you need to represent part-whole hierarchies and want clients to treat individual objects and compositions uniformly.

Try it yourself

#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include "Organization.h"

int main() {
    // Read inputs
    std::string companyName;
    std::string departmentName;
    std::string employee1Input;
    std::string employee2Input;
    
    std::getline(std::cin, companyName);
    std::getline(std::cin, departmentName);
    std::getline(std::cin, employee1Input);
    std::getline(std::cin, employee2Input);
    
    // Helper lambda to parse "name,salary" format
    auto parseEmployee = [](const std::string& input) -> std::pair<std::string, int> {
        size_t commaPos = input.find(',');
        std::string name = input.substr(0, commaPos);
        int salary = std::stoi(input.substr(commaPos + 1));
        return {name, salary};
    };
    
    auto [name1, salary1] = parseEmployee(employee1Input);
    auto [name2, salary2] = parseEmployee(employee2Input);
    
    // TODO: Create the company as a top-level Department
    
    // TODO: Create a sub-department
    
    // TODO: Create two Employee objects using the parsed data
    
    // TODO: Add employees to the sub-department
    
    // TODO: Add the sub-department to the company
    
    // TODO: Display the entire organization structure
    
    // TODO: Print the total salary in format: Total Salary: $[amount]
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming