What It Is
The Composite pattern treats single things and groups of things through the same interface. A file has a size; a folder full of files (and folders) has a size too — so both implement size(), and code that sums sizes never asks which one it holds.
Composites naturally form trees: groups contain leaves and other groups, to any depth.
Example: Files and Folders
The Item interface has one method. A file answers with its own size; a folder asks everything inside and adds it up — recursion falls out for free.
One interface for leaf and group
$root->size() walks the whole tree without one type check: the folder does not care whether an item is a 4 KB file or another folder hiding ten more.
Output: PHP script
<?php
interface Item {
public function size(): int;
}
class File implements Item {
public function __construct(
private string $name,
private int $size,
) {}
public function size(): int {
return $this->size;
}
}
class Folder implements Item {
public function __construct(
private string $name,
private array $items,
) {}
// A folder's size is the sum of everything inside — files or folders.
public function size(): int {
$total = 0;
foreach ($this->items as $item) {
$total += $item->size();
}
return $total;
}
}
$docs = new Folder("docs", [
new File("notes.txt", 4),
new File("plan.md", 6),
]);
$root = new Folder("root", [
new File("readme.md", 2),
$docs,
]);
echo "docs size: ", $docs->size(), " KB\n";
echo "root size: ", $root->size(), " KB\n";
When to Use It
Use it for tree-shaped data where groups and single items answer the same questions — file systems, menus with submenus, UI layouts, organization charts.
Skip it for flat lists. An array with a loop needs no pattern.
Pros and Cons
Pros: client code stays flat and simple; trees of any depth come for free; new leaf or group classes plug into the same interface.
Cons: the shared interface can force awkward methods on leaves (what is a file's "add child"?); very general composites become hard to constrain.