Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decorator Pattern, via inheritance or dependency injection?

Tags:

Right now I am studying the common design patterns and for the most part I understand the purpose of the decorator pattern. But what I don't get is, what is the purpose of wrapping an existing object in a decorator class?

Consider this scenario, since Progress is part of the observer pattern, I want to limit the amount of updates to its subscribers to prevent the UI thread from locking.

So I have decorated the class to only update once every 50 milliseconds.

public class ProgressThrottle<T> : Progress<T>
{
    private DateTime _time = DateTime.Now;
    public ProgressThrottle(Action<T> handler) : base(handler)
    {
    }

    protected override void OnReport(T value)
    {
        if (DateTime.Now.AddMilliseconds(50) < _time)
        {
            base.OnReport(value);
            _time = DateTime.Now;
        }
    }
}

public class ProgressThrottle2<T> : IProgress<T>
{
    private DateTime _time = DateTime.Now;

    private readonly IProgress<T> _wrapper;

    public ProgressThrottle2(IProgress<T> wrapper)
    {
        _wrapper = wrapper;
    }

    public void Report(T value)
    {
        if (DateTime.Now.AddMilliseconds(50) < _time)
        {
             _wrapper.Report(value);
            _time = DateTime.Now;

        }
    }

Both classes accomplish the same thing, except I find the first version better because it allows me to use the base constructor for setting the delegate for progress updates. The base class already supports overriding the method, so what is the need for me wrap the object?

Are both classes example of the decorator pattern? I would much rather use the first option but I rarely see examples in that manner.

like image 906
Ambidex Avatar asked Dec 11 '17 04:12

Ambidex


People also ask

Does Decorator design pattern use inheritance?

We use inheritance or composition to extend the behavior of an object but this is done at compile time and its applicable to all the instances of the class.

Which decorator is used for dependency injection?

Dependency injection, or DI, is one of the fundamental concepts in Angular. DI is wired into the Angular framework and allows classes with Angular decorators, such as Components, Directives, Pipes, and Injectables, to configure dependencies that they need.

Why would you favor the decorator pattern over inheritance?

In contrast to inheritance, a decorator can operate on any implementation of a given interface, which eliminates the need to subclass an entire class hierarchy. Furthermore, the use of the decorator pattern leads to clean and testable code (see Testability and Other Benefits sections).

Is dependency inject a design pattern?

In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.


1 Answers

Imagine you have n different implementations of the IProgress<T> interface.

For the sake of this example, let's consider two implementations:

  • EndpointProgress<T>, this would poll an endpoint and Report every time the response is different.
  • QueryProgress<T>, this would execute a database query periodically and Report every time the result is different.

In order to throttle both of these implementations using your first approach, you'd have to create two implementations of your ProgressThrottle<T>, one inheriting from EndpointProgress<T>, and another one inheriting from QueryProgress<T>.

In order to throttle both of these implementations using the second approach you'd just have to use a wrapped instance of EndpointProgress<T> and QueryProgress<T>.

var throttledEndpointProgress = new ProgressThrottle2<int>(new EndpointProgress<T>());
var throttledQueryProgress = new ProgressThrottle2<int>(new QueryProgress<T>());

Edit:

So in a scenario were I am certain I will not extend a class more than once to add functionality, is it acceptable to not use a wrapper?

I still would use the second implementation of the decorator (I'm not even sure of the first implementation would be considered the decorator pattern) for several reasons:

  • S.O.L.I.D. principles' open/closed principle states that:

    Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

    I you have to modify your current Progress implementation in order to extend it, you are violating Open/Closed.

  • Having ProgressThrottle inherit from Progress means that every time Progress' constructor changes, ProgressThrottle also needs its constructor changed.

  • By using wrapper decorators, you're able to compose and combine decorators. Let's consider an implementation of IProgress<T> that logs every onReport call. You could —based on configuration, environment, etc— compose these decorators in different ways to achieve different goals:

    var progress1 = new LoggingProgress<int>(
        new ProgressThrottle<int>(new Progress<int>())
    );
    var progress2 = new ProgressThrottle<int>(
        new LoggingProgress<int>(new Progress<int>())
    );
    

    Here, progress1 will only log the throttled reported progress. progress2 will log all the reported progress, but will report in a throttled manner. Depending on what your objectives are you might want one implementation or the other; or you might want both of them, one for diagnostics in staging and another one for prod, but the most important thing is that you don't have to change the implementation of your decorator in order to change this behavior.

like image 170
Maximo Dominguez Avatar answered Oct 15 '22 10:10

Maximo Dominguez