The Smart Way of Using the Decorator Pattern in C#

preview_player
Показать описание


Hello, everybody, I'm Nick, and in this video, I will show you how you can change your code's behaviour without changing the code directly. We will be using a technique called Decoration and we will also use a new .NET 8 feature to make it even more interesting.

Subscribe to Amichai: @amantinband

Don't forget to comment, like and subscribe :)

Social Media:

#csharp #dotnet
Рекомендации по теме
Комментарии
Автор

I don't like it. The main disadvantage is that the decorator must know what it is decorating (by specifying a key to a What if we want to have multiple implementations of some interface and all of them must be decorated in the same way? In my opinion it is indirect dependency by a string key. Technically it does not differ from extending a class instead of implementing the same interface and it's even worse because the dependency is hidden in a parameter of some attribute.

hoolio
Автор

I don't like the key approach with the decorator owning the key. It would rapidly become a mess with multiple level of decorator.

deesmon
Автор

This is a bit like Inception, except you never go more than 1 level. Any deeper is a path to madness...

CameronOwen
Автор

It's coupling the composition root with the implementation; much better to use named registrations and in turn register the decorator using a factory delegate (that in turn resolves the named registration). Regardless, the decorator pattern is one of the most productive and important patterns in modern API development.

andersborum
Автор

This api doesn't really work well with decorator pattern since it supports only one level of decoration. The whole point of decorator is that you can stack as many of them as you want.

Edit: I have an idea:
services.Decorate<IInterface, BottomClass>()
.With<Decorator1>()
.ThenWith<Decorator2>()
.ThenWith<Decorator3>();

markovcd
Автор

I really dislike the way you hook up the decorated service in the implementation. That breaks the whole idea of having your application composition separate from your implementation.
You're better off putting that in your extension method, resolve the og there. But you can't really do that with the vanilla DI framework, because your factory needs to be aware of all the parameters while most of the time your decorator will want some other constructor parameters too.

GuidoSmeets
Автор

5:09 - For anyone even considering using labels like that, instead just use a do-while with a break condition.

KangasniemiJerri
Автор

I have watched most of your videos. I am not sure that I agree with the philosophy of this but the idea to do it this way is brilliant. Keep it up.

MrJeffrey
Автор

Decorators = method interception = pipelines = all good. I don't buy the developer confusion argument against decorators, I think the problem can be overcome with structuring and awareness of configured behaviors.

What I don't like about this example is the fixed key that the decorator needs to know, which limits the decorator to a single depth because of a dependency on the original/core implementation.

Decorators as behavioral extension points should be capable of arbitrary depth.

petewarner
Автор

I'm not sure when is good to have the decorator pattern, and when is not. Recently I begin to work on an new company where they were using the Decorator Pattern for translations where basically are 5 layers to find translations (custom -> providers -> another providers -> etc) and was really hard to debug and understand completely the code. For this kind of escenarios such as retry policies or cache layer I think is ok.

leandrowitzke
Автор

Great video. The main issue is coupling introduced by the key which kills the idea of decoration as you're now tight to a single implementation. I believe keeping it explicit in the DI registration or using a factory is better

youcefmerzoug
Автор

Amazing video. I have a suggestion to even avoid 2 issues with this approach:

1. To avoid using hardcoded keys to be passed in the registration, we can add a static Key property in the Interface. This will allow to pass the Key property instead.
i.e. services.AddDecoratedSingleton<IMyService, DecoratedService, MyService>(MyService.Key), something like that.

2. To avoid boxing, why not using generics. In this case the Interface may be of type generic, so the Key property I mentioned before may be of type K.

diego_samano
Автор

My preferred decorator pattern involves using DispatchProxy. That way I can (e.g.) wrap all calls in an exception logger, or timing, or security without having to implement all the interfaces I want to decorate. It will handle any method on the interface using it automatically. The hardest part if elegantly registering it all (still working on that aspect of it).

I'm sure that since it's using reflection, it's not the fastest, but the laziness aspect really pays off.

AmateurSpecialist
Автор

Seems to me that Factory Pattern is also a fit solution for resolving dependencies of a decorated service like this. Correct me if I'm wrong. I'm still learning design patterns and those are really great to use :D

hnz
Автор

I guess the example here is little hard to digest. I can see one useful scenario while api versioning(for V1 i can have one implementation and for V2 i can have different implementation). Thanks for sharing it :)

harisuru
Автор

Used to have decorators done but was using Autofac. It was really simple

MaximilianDeister
Автор

What about using generics instead of string keys...? So the IService will have a IService<T> which gets registered to the actual implementation, then your IService can be registered with your "root entry" actual implementation. There can be many variations using this pattern

schalkvwyk
Автор

This seems like it would be good for logging, retries, audit trails, and other non critical pieces of functionality. I wouldn't expect this type of approach would necessarily be useful or even ideal for business logic. As you pointed out, the main service should really only know how to do it's main job and the rest of the fluff can be moved elsewhere and this approach could work for that. I think it may get a little out of control if you have multiple different kinds of fluff that need to be removed. Logging would need it's own layer, retries their own layer, caching, etc... The more fluff you have, the more layers you have which can just make the code much harder to understand I think.

stevenodlum
Автор

I keep using scrutor library for decorating things, but for 1 lvl of decoration it is nice feature

alirezanet
Автор

In my humble opinion, the fact that we have to go through all these hoops and complexity just to get it to work screams KISS to me. Do it the way we did it before all these dependency injection frameworks. Instantiate your objects and pass them to the ctors. Easy. We see the flow. Less lines of code. The principle of KISS.

Sayuri