To follow good design principles and structure when you develop applications is something I advocate.
As the development process goes on we add new functionality to our applications: it could be a feature request from your client or it could be improvements that you’ve suggested. No matter what, these upcoming changes will also change the already given codebase. When changes are introduced we want this to be as easy as possible.
In this post I’m going to show you an example of how you can utilize the decorator pattern with the powerful IoC container Autofac in scenarios where you want to extend functionality to an existing feature.
Lets say you are about to build a service that serves products. We will call this service IProductRepository and for simplicity we only use one method called GetById. It could look something like this with an implementation connected to it:
Normally you would register your concrete implementation XmlProductRepository as IProductRepository with the following code line to support the dependency inversion principle:
Lets say that you after a while need some logging when products are fetched. How do you accomplish this?
It is tempting to just modify the implementation XmlProductRepository by injecting a logging service like this:
But that violates the open/closed principle. The principle states that you are not allowed to modify classes, but you can extend them. Here the decorator pattern is really handy.
Instead of cluttering down your original implementation you extend it by adding a logging proxy, and register it as a decorator of it.
You will have to modify one code line though, and that one is the registration of the original implementation. It will need to be a named registration now so we can refer to it when we register our decorator:
And then connect and register the decorator:
The key that we use to refer to the implementation here is productRepository. When you have this code in place the first class that will be invoked when you inject your IProductRepository is the LoggingProductRepositoryProxy, and as we have injected the inner implementation to it it will just work as a proxy (russian doll style) following the open/closed principle.
Lets say that you experience your XmlProductRepository slow and you need to cache your products for a while. How would you implement that? It is pretty easy to just add another decorator on top of your logging implementation:
The decorator pattern in really powerful when it comes to supporting cross cutting concerns within applications.
Did you find this post useful? You know what to do! 👏