design patterns · medium
Composite Pattern
Composite is a structural GoF pattern that composes objects into tree structures to represent part-whole hierarchies and lets clients treat individual objects (leaves) and compositions of objects (containers) uniformly through a shared interface. It has three roles: Component (the common interface, e.g. Node, declaring operations valid on both leaves and groups like size()); Leaf (a terminal element such as File that implements the operation directly); and Composite (a container such as Folder that holds child Components and implements each operation by delegating recursively — size() sums its children's size()). Because every child is just a Component, a Composite can contain leaves and other composites interchangeably, yielding arbitrary-depth trees over which operations recurse naturally; the client never branches on whether it holds one item or a subtree. It models filesystems, GUI widget trees, scene graphs, and nested menus. The pattern's classic design tension is where to declare child-management methods (add/remove/getChild): on the Component for transparency (uniform client, but Leaf must reject them) versus only on the Composite for type safety (but the client must downcast). Other costs are an interface that can grow overly general and the risk of stack overflow on naive recursion over very deep trees.
Composite (structural): compose objects into part-whole TREES and let clients treat a single Leaf and a whole Composite uniformly through one Component interface. A Composite holds child Components and implements each operation by recursing over them (Folder.size() sums its children). Removes leaf-vs-group branching in the client. Central trade-off: child-management methods on Component (transparent, Leaf must error) vs only on Composite (type-safe, needs downcast).
The code
// Composite — treat individual objects and COMPOSITIONS of objects// uniformly. A tree where leaves and branches share one interface.struct Node { // Component virtual int size() const = 0; // works on leaf AND group virtual ~Node() = default;};
class File : public Node { // Leaf int bytes_;
public: explicit File(int b) : bytes_(b) {} int size() const override { return bytes_; }};
class Folder : public Node { // Composite std::vector<std::unique_ptr<Node>> kids_;
public: void add(std::unique_ptr<Node> n) { kids_.push_back(std::move(n)); } int size() const override { // recurse over children int t = 0; for (auto& k : kids_) t += k->size(); // leaf or folder — same call return t; }};
// Client treats one File and a whole Folder tree identically:void print(const Node& n) { std::cout << n.size();}What this lesson walks through
- 01Intent — treat part & whole alike
- 02Structure — Component, Leaf, Composite
- 03One call recurses the whole tree
- 04Gotcha — leaf safety vs uniformity
A Composite lets a client treat a single object (Leaf) and a group of objects (Composite) through the SAME interface. Folders contain files and other folders — but you call size()/render() identically on any node.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of Composite Pattern and 100+ animated C++ interview lessons.