The Decorator pattern is one of our toolbox’s simplest yet most powerful design patterns. It augments existing classes without modifying them. A decorator is an independent block of logic that we can use to create complex and granular object trees that fit our needs.We also explored the Scrutor open-source library to assist us in registering our decorator with the container.The Decorator pattern helps us stay in line with the SOLID principles (and vice versa), as follows:
- S: The Decorator pattern suggests creating small classes to add behaviors to other classes, segregating responsibilities, and fostering reuse.
- O: Decorators add behaviors to other classes without modifying them, which is literally the definition of the OCP.
- L: N/A
- I: By following the ISP, creating decorators for your specific needs should be easy. However, implementing the Decorator pattern may become difficult if your interfaces are too complex. Having a hard time creating a decorator is a good indicator that something is wrong with the design—a code smell. A well-segregated interface should be easy to decorate.
- D: Depending on abstractions is the key to the Decorator’s power.
Next, we explore the Composite pattern, which helps us manage complex objects’ structures differently than the decorator does.
Implementing the Composite design pattern
The Composite design pattern is another structural GoF pattern that helps us manage complex object structures.
Goal
The goal behind the Composite pattern is to create a hierarchical data structure where you don’t need to differentiate groups from single components, making the traversal and manipulation of the hierarchy easy for its consumers.
You could think of the Composite pattern as a way of building a graph or a tree with self-managing nodes.
Design
The design is straightforward; we have components and composites. Both implement a common interface that defines the shared operations. The components are single nodes, while the composites are collections of components. Let’s take a look at a diagram:

Figure 11.7: Composite class diagram
In the preceding diagram, Client depends on an IComponent interface and is unaware of the underlying implementation—it could be an instance of a Component or a Composite; it does not matter. Then, we have two implementations:
- Component represents a single element; a leaf.
- Composite represents a collection of IComponent. The Composite object uses its children to manage the hierarchy’s complexity by delegating part of the process to them.
Those three pieces put together create the Composite design pattern. Considering that it is possible to add instances of the Composite and Component classes as children of other Composite objects, it is possible to create complex, non-linear, and self-managed data structures with next to no effort.
You are not limited to one type of component and one type of composite; you can create as many implementations of the IComponent interface as you need. Then, you can even mix and match them to create a non-linear tree.