Composite is a structural design pattern that lets you compose objects into tree structures and allow clients to work with these structures as if they were individual objects.
The composite pattern makes sense only when your business model can be represented as a tree.
For instance, you have two objects:
Box can contain several
Products and a number of smaller
Boxes inside. These little
Boxes can also hold inside some
Products or even smaller
Boxes and so on.
Now, imagine that your
Boxes can be a part of order. How hard can it be to calculate the order total? You just take a large
Box, unwrap it and see what is inside:
ProductB, oh, another
Box, let's see what inside... Before too long, you will end up in a pile of duct tape and cardboard still trying to sum-up the price.
Isn't there a better way?
The Composite pattern suggests treating all different
Boxes through a common interface that has a method
Products, it will just return the product's price. But
Boxes will get much more interesting behavior. A Box will go over its content and ask every item its price. Naturally, if an item was a product, it will return the price right away. But if that was a smaller box, it will also start looking over its items asking for the price until it can return the total. Once the subtotal is calculated, a
Box can even add some extra to the full price, like a packaging cost.
Now, probably the greatest benefit of this approach is that you do not need to care much about the concrete objects that make a tree. Whether it is a single
Product or sophisticated
Box, you treat it through a generic interface. And the structure handles passing the requests down by itself.
Armies of most countries look like composite trees. On the lowest level, there are soldiers. They are grouped into squads. Several squads make a platoon. Platoons make a division. And finally, several divisions make an army.
Orders are given at the top of the hierarchy and passed down at each level until every soldier knows what needs to be done.
Component declares the common interface for both simple and complex elements of a tree.
Leaf is a basic element of a tree that does not have children.
Since they do not have anyone to delegate to, leaves often do most of the real job.
Container (aka Composite) is an element that has children: Leaves or other Containers. Containers do not know the type of their children since they work with them via Component interface.
Upon receiving a request, Containers delegate the work to their children, then process and sum-up the results before return it to Client.
Client uses all tree elements only through the Component interface.
Thus, the Client does not care whether it deals with a simple Leaf or a complex Container.
In this example, the Composite pattern helps to implement stacking geometric shapes.
CompoundGraphic is a container that can consist of any number of sub-shapes, including other compound shapes. The compound shape has the same methods as a simple shape. But instead of doing the actual work, it passes requests to its child shapes, recursively traversing over all immediate and distant sub-shapes. The container then sums up the results and returns it to the client.
The client works with all shapes through the interface, common to all types of shapes. This way the client code can work with very complex structures without coupling to the concrete classes of tree elements.
When you have to implement a tree-like structure that has simple elements and containers.
The Composite pattern offers two basic elements: simple leaves and complex containers that store other leaves or containers, and so on. Pattern forces the containers to work with all child elements through the common interface, which allows running operations recursively over the whole tree structure.
When Clients should treat simple and complex elements uniformly.
Thanks to the common interface between leaves and containers, Client code does not have to care about the type of object it works with.
How to Implement
Make sure that your business logic can be represented as a tree structure. Try to break it into simple elements and containers. Remember that containers can contain both simple elements and other containers.
Describe a common interface for all Components. It should contain operations that make sense for both simple and complex components.
Create a Leaf class that represents simple components. By the way, a program could have multiple leaf classes.
Create a Container class that has an array field for storing sub-components. The field should be able to store both Leaves and Containers, so make sure it is declared with a Component type.
While implementing the Component interface methods, remember that Container should be delegating most of its work to subcomponents.
Finally, implement methods to add/remove child elements to the Container.
Keep in mind, that these operations could also be put into the Component interface. It will violate the Interface Segregation Principle because these methods will be empty in Leaf class. But on the other hand, all of the tree components will become truly equal for the Client's standpoint.
Pros and Cons
- Simplifies the client code that has to interact with a complex tree structure.
- Makes easier adding new component types.
- Creates a too general class design.
Relations with Other Patterns
Decorator can be viewed as a degenerate Composite with only one component. However, Decorator adds additional responsibilities to the object while Composite just "summs up" of the same behavior executed over its children.
But they can also cooperate: Composite can use Decorator to change behavior of the tree components.