Decorator and Single Responsibility Perfectly Play Together.
It is so often to write service with business logic and give it a lot of responsibility. When I started my career, I was guilty of doing such a thing. And testing a complex service that handles many different logical parts is like a nightmare. Step by step, trying to make my life easier, I`ve reopened decorator ( design pattern ) for myself.
What is a Decorator?
A decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.
It is pretty clear from the definition that decorator should be our silver bullet when we need to add some behavior for the existing codebase or when we want to design a good system. That doesn`t mean using a decorator for any small piece of logic that we want to add. As developers, we should consciously design our system and avoid writing tons of structure code just for a small piece of logic.
Is Single Responsibility related?
Let me recall what the definition of Single Responsibility is?
The single-responsibility principle (SRP) is a computer-programming principle that states that every module, class, or function in a computer program should have responsibility for a single part of that program’s functionality, and it should encapsulate that part.
Take a moment to read about SOLID principles.
And here, we get a perfect fit. With single responsibility, we design our components so that they are doing just one thing. With a decorator, we add additional behavior to our logical pipeline, and it`s like a chain of small logic pieces executed one by one.
Here are some examples from my experience when the decorator played exceptionally well.
Basic Setup
For example, we can take a situation when we store data in a database. There are always logical operations to prepare data and store it in a database. Also, later, we want to send a notification that the new entity was stored in the database:
1 | public class Entity |
The current code is a good example where the decorator fits the best. There are two logical operations to store entities in the database and send notifications. We should create ServiceDecorator, responsible for sending notifications, and there is no need to add additional logic for our existing service. It stays as it is without any modification. We are getting a simple new service that can be covered with a separate set of unit tests.
1 | public class ServiceDecorator : IService |
Conclusion
Separating logic pieces using a design pattern decorator helps us to make code more maintainable and testable. Consider using a decorator as your reliable tool when you face a situation to add new logic.