Already done and overdone. But never enough if you want to give it another try.
Consider a useful pattern that has been out there for some time, and it dared to never show its true form to you. Well, this has happened and it is still happening. We see it all around. And these patterns can be tricky to understand, to implement, to decide when to be applied and when not to be applied. They can oversimplify or even overcomplicate your solution. As already pointed by the title the topic here is the decorator pattern.
So, let’s just give it another shot.
Photo by Omar Flores on Unsplash
Decorator Pattern
So what is the Decorator Pattern?
Consider having an already existing implementation of an entity. An implementation that you do not want to modify or introduce changes to, that do not involve the domain itself. This can be a temporary promotion, which could involve changing the data going in and out from an entity. This can also be a technical dependency such as a database retry pattern, change tracker, or some additional functionality outside of the scope of the entity. This approach can be applied on an already implemented, tested, and documented implementation, which you intend to avoid changing. Or on a new implementation when the use case demands it. And this is where the pattern comes in handy, extending existing functionality or separating functionality that just doesn’t fit in with the entity’s logic. So let´s consider a simple change tracker addition to our entity. The purpose would be to track all changes happening to what has been changed and by who, so that there is some accountability to the system changes.
So let´s just simply first imagine a basic implementation.
What simply this pattern does, is provide us a way to extend the behavior of the class in question. It simply wraps the original class around a wrapper. By doing this we can change the behavior of an individual entity, in comparison with some other approaches such as inheritance, where you modify all the instances of the class in question.
The good thing about the pattern is that it abides by some of the principles we software engineers tend to follow, which try to keep the solution simple(sometimes may be complicated, but the initial goal is to keep it simple):
- Adhere to the SRP (Single Responsibility Principle)
- Adhere to OCP (Open Closed Principle)
Implementation
In the implementation we will use a basic example, avoiding generics or any additional approach. They can be quite useful, but they also might obscure the example. And the goal is to get an understanding of the Decorator Pattern, avoiding any overhead. But let’s keep in mind, that all of the additional approaches such as generics are very useful. And play its role in each implementation when required, but for the clarity of the example here we will avoid any overhead.
The implementation will be consisting of:
Program.cs
, which will contain the initialization of the pattern.- An interface of the class that we want to decorate.
- The class that we plan to decorate and add a basic method to.
- And the decorator
ChangeTrackerDecorator.cs
itself. - And the necessary example models.
Pre Implementation
Let´s first consider the existing implementation. Before the decorator has been applied.
The product implementation consists of a skeleton structure, just for this purpose.
Implementing the Decorator Pattern
We start by adding an interface for the already created ProductService.cs
. And right after follows the implementation of the decorator class. The decorator class implements the same interface as the class we intend to decorate.
This can be visually imagined as follows.
What happens and can be seen here is that the implementation can be seen wrapping around the concrete product service class. By introducing the decorator, we avoid changing the concrete implementation and abide by the OCP principle.
Decorator Pattern Implementation with CQRS
As an additional example, would be useful to see how the Decorator Pattern can be used with CQRS (Command Query Responsibility Segregation). To avoid getting sidetracked, we won’t be talking much about it. But there are a lot of great posts and documentation out there. This approach involves separating all commands and queries, as the name would suggest. Some documentation and references can be found on the Microsoft Documentation.
A quick visual overview of how the components interact between each other.