Menu
Coddy logo textTech

Composite Pattern

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 80 of 91.

The Composite Pattern is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects. It's perfect for representing part-whole hierarchies where you want to treat single objects and groups of objects uniformly.

Consider a file system. A folder can contain files and other folders.

When you calculate the total size, you want to call the same method on both files and folders, and folders should recursively sum up their contents. The Composite Pattern makes this seamless by giving both leaves (files) and composites (folders) the same interface.

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

Now you can build tree structures and treat them uniformly:

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize();

Output:

1810

The client code calls getSize() without caring whether it's dealing with a single file or a deeply nested folder structure. The pattern handles the recursion internally, making complex hierarchies easy to work with.

challenge icon

Challenge

Easy

Let's build an organization chart system using the Composite Pattern. Companies have a hierarchical structure where departments can contain employees and other sub-departments — a perfect tree structure where you want to calculate totals uniformly across the entire organization.

You'll organize your code across four files:

  • OrganizationUnit.php — Define an OrganizationUnit interface that both individual employees and departments will implement. It should have two methods: getName(): string and getSalary(): int. This common interface allows you to treat single employees and entire departments the same way when calculating costs.
  • Employee.php — Include the OrganizationUnit interface and create an Employee class that implements it. An employee is a leaf node in your organization tree — they have a name and salary but cannot contain other units. Use constructor promotion to accept the name and salary, and implement both interface methods.
  • Department.php — Include the OrganizationUnit interface and create a Department class that implements it. A department is a composite that can contain both employees and other departments. Your department should:
    • Accept a name in its constructor
    • Have an add(OrganizationUnit $unit): void method to add employees or sub-departments
    • Implement getSalary() to return the total salary of all units it contains (recursively summing everything inside)
  • main.php — Include the Employee and Department files. You'll receive one input: a JSON string representing the organization structure.

    The JSON will be in this format:

    {"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}

    Each key is a department name, and its value is an array of employees with their names and salaries.

    Create a root Department called "Company". For each department in the JSON, create a Department, add its employees, then add that department to the Company. Finally, print the total company salary in this format:

    Total Salary: [amount]

The beauty of the Composite Pattern is that calling getSalary() on the Company department automatically calculates the sum across all nested departments and employees — the client code doesn't need to know the internal structure.

Cheat sheet

The Composite Pattern is a structural design pattern that composes objects into tree structures and allows treating individual objects and groups uniformly.

It's ideal for part-whole hierarchies where you want the same interface for both leaf nodes and composite nodes.

Key Components

  • Component Interface — Defines common operations for both leaves and composites
  • Leaf — Represents individual objects that cannot contain other objects
  • Composite — Contains children (leaves or other composites) and implements operations by delegating to children

Example: File System

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

Usage

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize(); // 1810

The client calls getSize() uniformly on both files and folders. The composite handles recursion internally, making complex hierarchies simple to work with.

Try it yourself

<?php
require_once 'Employee.php';
require_once 'Department.php';

// Read the JSON input
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Create a root Department called "Company"

// TODO: Loop through each department in the JSON data
// For each department:
//   - Create a new Department object
//   - Add each employee to that department
//   - Add the department to the Company

// TODO: Print the total salary in the format: Total Salary: [amount]

?>
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