Easy patterns: Composite
This article is created in continuation of easy patterns series description and presents a Composite pattern which solves the problem of how to use recursive composition among set of different objects so clients don’t have to make this distinction.
Creational patterns:
Structural patterns:
Composite (this article)
Behavioral patterns:
The main essence
The Composite pattern helps to operate separate objects in unified order. This is possible due to special class object which declares common representation operations for all participants in composition set.
This pattern includes three main roles:
- Component — declares the interface for objects in the composition and implements the default behavior (based on Composite)
- Leaf — represents primitive objects in the composition
- Composite — implements child-related operations in the Component interface and stores Leaf (primitive) objects.
Clients are using the Component interface to interact with objects in the composition set. If the recipient is a simple object (Leaf), then request is handled directly by this simple object. If the recipient is a Composite, it usually forwards request to child components. For composite is also typical to perform some pre- and post-hook operations.
Example of use
In this example we handle getting an average salary of employees for a concrete organization. An organization stores employees in an array and can count average salary of all employees.
This is a simple representation of usage, when organization can store only typical employee (with name
and getSalary
method).
To feel all the power of a pattern just imagine a situation when a parent organization can store not only employees but also a child organizations. All what is needed for that is just to implement getSalary
method for such child organization which would return an average salary of its internal employees. In such way calling avgSalary
on parent organization wouldn’t be broken and a result would be the average salary over all the emloyees whether working on parent organization directly or through child organizations.
Profit
This pattern makes the client simple. Clients can work with individual either composite structure in similar manner. This makes client code simpler because it avoids having to write case statement functions over the classes for simple and complex objects.
This pattern makes it easy to add new kinds of components. Newly created items work automatically with existing structures and client code.
Weak places
From the other hand this pattern can make your design more generic. It makes harder to restrict the components of a composite (for example you want a composite to include only certain components). This approach enforces you to use run-time checks instead.
Conclusion
The Composite pattern are often used in tandem with other patterns like:
- Decorator — when used together with a Composite they usually have a common parent, so decorators will have to support the Component interface like Add, Remove, etc.
- Flyweight — lets you share components.
- Iterator — lets you traversing composites.
- Visitor — localizes operations and behavior that in other case would be distributed across composite classes.
If you found this article helpful, please hit the 👏 button and feel free to comment below!