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:
1810The 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
EasyLet'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 anOrganizationUnitinterface that both individual employees and departments will implement. It should have two methods:getName(): stringandgetSalary(): 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 anEmployeeclass 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 aDepartmentclass 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): voidmethod 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
Departmentcalled"Company". For each department in the JSON, create aDepartment, 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]
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators13Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite PatternRepository Pattern2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern