Composite
Composite is a structural pattern that lets individual objects and groups of objects be treated through the same interface. It builds a tree structure while allowing the client to call the same method without caring whether the target is a leaf node or a group.
βΆArchitecture Diagram
π StructureDashed line animations indicate the flow direction of data or requests
How do you calculate the total size of a folder? Inside a folder there can be files, and there can be subfolders. Inside subfolders there can be more folders still. If the client has to check 'is this a file or a folder?' every time and branch accordingly, the code is littered with type checks and conditionals. Every new node type forces updates to every branch. As tree depth grows, this complexity scales exponentially.
This problem first surfaced prominently in early GUI framework design. Screen elements could be leaf widgets like buttons or text, or containers like panels or windows that hold other widgets. If rendering code branched on 'is this a container or a single widget?' every time, rendering complexity grew proportionally to the number of widget types. Smalltalk's MVC framework and early GUI libraries like ET++ encountered this repeatedly, leading GoF to formalize the recursive composition structure that treats parts and wholes uniformly as the Composite pattern. Today, React's component tree, the DOM tree, and file-system APIs all stand on this principle.
Composite's structure resembles Russian nesting dolls (matryoshka). A large doll contains a smaller doll, which contains an even smaller one. But unlike matryoshka, from the outside every doll looks the same regardless of size. Three roles make up the structure. The Component interface defines the shared contract for all nodes. A Leaf is a terminal object with no children that performs the actual work. A Composite holds a list of child Components and, when its operation() is called, recursively delegates the same operation() to every child. For example, calling `getSize()` on a folder causes the folder to call `getSize()` on each child file and subfolder and sum the results. Subfolders repeat the same process. The client makes a single request at the top, and the entire tree handles itself.
Composite and Decorator are frequently compared because both use recursive composition. The difference lies in purpose. Composite is for grouping multiple objects and treating them as one; Decorator is for layering functionality onto a single object. A Composite's tree can have many children, while a Decorator always wraps exactly one target. Iterator is also related. Composite builds the tree structure; Iterator separates the method of traversing that structure. Building the structure is Composite's role; deciding the traversal strategy is Iterator's. Determining whether you need a tree structure or just need to separate traversal is the first question that distinguishes the two.
Commonly Compared Concepts
Decorator
A structural pattern that dynamically adds behavior to an object
Both use recursive composition, but Composite groups multiple objects into a tree while Decorator layers functionality onto a single object.
Iterator
A pattern that traverses collection elements sequentially while hiding the internal structure
Composite is the pattern that builds a tree structure, while Iterator is the pattern that separates how to traverse that structure.
Composite appears almost anywhere data or UI naturally takes tree form. Using the same interface to copy, move, and delete files and folders in a file explorer; nesting React components within components and applying the same lifecycle; moving and resizing shape groups just like individual shapes in a graphics editor -- all are Composite. The signal for adoption is clear: when code that branches on 'is this a single object or a group?' is repeated in multiple places, or when you need to apply the same operation uniformly across a tree of nodes. However, applying Composite to every hierarchy intentionally hides the difference between Leaf and Composite, which can bloat the interface with operations that are meaningful only to one type. First determine whether a tree structure is actually needed or whether a simple list would suffice.