Decorator pattern: Why do we need an abstract decorator?

11,282

Solution 1

It enables the decoration of the base class independently with various decorators in different combinations without having to derive a class for each possible combination. For example, say you want your Beverage with milk and nutmeg. Using decorators based on the abstract decorator class, you merely wrap with with Milk and Nutmeg decorators. If it was derived from Beverage, you'd have to have a MilkWithNutmegBeverage class and a MilkBeverage class and a NutmegBeverage class. You can imagine how this explodes as the number of possible combinations increases. Using the abstract decorator implementation reduces this to just one decorator class implementation per decoration.

Solution 2

Better one and a half year late than never:

A base class for decorators of a certain interface is not necessary.

However, it is very useful to have:

  • for one thing, as a means of documenting that classes derived from it are decorators of the interface in question

  • but mostly, because decorators usually do not need to add functionality to every single method of the decorated interface.

So, a base decorator class allows derived decorators to implement only those methods of the interface to which they actually need to add some functionality, leaving the rest of the methods to the base class to provide a default implementation for. (Which simply delegates the call to the decoree.)

Contrast this with writing decorators that implement the decorated interface from scratch, where the compiler requires that you provide an implementation for every single method of the interface, whether your decorator will be adding any functionality to it, or not.

It is that simple, really.

Solution 3

I was wondering the same thing. Going back to the source, GOF Design Patterns, I see this under 'Implementation' in the Decorator chapter:

"Omitting the abstract Decorator class. There's no need to define an abstract Decorator class when you only need to add one responsibility. That's often the case when you're dealing with an existing class hierarchy rather than designing a new one. In that case, you can merge Decorator's responsilibility for forwarding requests to the component into the Concrete Decorator."

So at least in that case, it seems that GOF agree with you :-)

I'm not sure what the meaning of 'one responsibility' is. I'm not sure if more than 'one responsibility' would mean one concrete decorator that has more than one responsibility or more than one concrete decorator, each with its one responsibility. Either way, I don't see why the abstract Decorator is necessary. My guess is that tvanfosson's answer (in his comment on his own answer) is the right one - that once you start creating a number of decorating classes, it clarifies the design decision to group them under a superclass. On the other hand, where there is just one class, it perhaps makes the design decision less clear if you add in a second class that just sits as a pointless middle-man between base component and decorator (having said that, it's fairly likely that you'll want to add more at some point, so maybe better to include the abstract decorator even in the single case...)

At any rate, seems like it's to do with making the design clear, rather than the being the difference between the code working and not.

Solution 4

(A bit late to your question..)

I also spent quite a while to try to figure out an answer. In my case the non-concrete Decorator extends the class to be decorated ("decoree"), instead of an interface common to both the decoree and the Decorator.

After reading from different sources, it seems to me that, besides what tvanfosson said, the reason to have the concrete decorators extend an abstract, or more general, decorator is so that we don't repeat the "delegation" code over and over again.

[Beverage]<———————[(abstract) CondimentDecorator]<—————[Milk]
[decoree ]——————<>[ adds code to efficiently    ]
                  [ forward calls to decorated  ]<—————[LemonJuice]
                  [ instance of Beverage        ]

In your case, your abstract decorator would extend the decoree and would implement the same interface as the decoree, delegating/forwarding all method calls to it. Then, the concrete decorators that you may build, would only need to implement those methods where you would want to do something different than just forward the method call to the decoree.

I hope I was clear.. :-S

Personally, I'm a bit disappointed at the need to repeat the decoree's interface in the decorator. This adds some coupling, since any time the decoree's interface changes (like getting more methods), the decorator needs to catch up.

In PHP 5.3.2 (yes, I know your question is related to Java), though, it should be possible to dynamically catch all method calls and forward them all, without the Decorator needing to know which methods are being called (in a not a very efficient way, though, having to use the Reflection API). I guess this is possible in Ruby and other languages already.

PS: This is my first answer ever in SO! ^_^

Solution 5

The base Decorator makes it easier to create additional decorators. Imagine that Beverage has dozens of abstract methods, or is an interface, say stir(), getTemperature(), drink(), pour() and the like. Then your decorators all have to implement these methods for no other reason than to delegate them to the wrapped beverage, and your MilkyBeverage and SpicyBeverage each have all those methods.

If instead you have a concrete BeverageDecorator class that extends or implements Beverage by simply delegating each call to the wrapped Beverage, subclasses can extend BeverageDecorator and only implement the methods they care about, leaving the base class to handle delegation.

This also protects you if the Beverage class (or interface) ever gains a new abstract method: all you need to do is add the method to the BeverageDecorator class. Without it, you would have to add that method to each and every Decorator you had created.

Share:
11,282
oym
Author by

oym

Updated on June 13, 2022

Comments

  • oym
    oym about 2 years

    This question was asked already here, but rather than answering the specific question, descriptions of how the decorator pattern works were given instead. I'd like to ask it again because the answer is not immediately evident to me just by reading how the decorator pattern works (I've read the wikipedia article and the section in the book Head First Design Patterns).

    Basically, I want to know why an abstract decorator class must be created which implements (or extends) some interface (or abstract class). Why can't all the new "decorated classes" simply implement (or extend) the base abstract object themselves (instead of extending the abstract decorator class)?

    To make this more concrete I'll use the example from the design patterns book dealing with coffee beverages:

    • There is an abstract component class called Beverage
    • Simple beverage types such as HouseBlend simply extend Beverage
    • To decorate beverage, an abstract CondimentDecorator class is created which extends Beverage and has an instance of Beverage
    • Say we want to add a "milk" condiment, a class Milk is created which extends CondimentDecorator

    I'd like to understand why we needed the CondimentDecorator class and why the class Milk couldn't have simply extended the Beverage class itself and been passed an instance of Beverage in its constructor.

    Hopefully this is clear...if not I'd simply like to know why is the abstract decorator class necessary for this pattern? Thanks.

    Edit: I tried to implement this, omitting the abstract decorator class, and it seems to still work. Is this abstract class present in all descriptions of this pattern simply because it provides a standard interface for all of the new decorated classes?

  • oym
    oym over 14 years
    I'm not sure I understand. Yes, I am deriving 'Milk' from 'Beverage'; but 'Milk' is also being passed an instance of the 'Beverage' class which I would think is allowing me to wrap the classes as would be done with an abstract decorator class.
  • oym
    oym over 14 years
    what I am trying to say is that with the abstract decorator class, all the new decorated classes inherit from it..so why can't we just remove the middle man (and have them inherit directly from 'Beverage' PLUS passing them an instance of beverage as was done before)?
  • tvanfosson
    tvanfosson over 14 years
    I suppose you could do that, but as a child class it need not provide any additional behavior. Conceptually, the decorator, even though it implements the same interface, is not actually an instance of the decorated class, but rather a helper class that augments the decorated class. Basing it off a different abstract class exposes this design aspect and makes it clearer how new decorations are to be added. Implementing the decorations as child classes blurs this distinction and leaves the implementation open to child classes that may break the decoration pattern unwittingly.
  • Michael Blackburn
    Michael Blackburn almost 13 years
    @es11: Ok, so you're deriving Milk from 'Beverage' -- what about 'Nutmeg' ? What happens if a consumer attempts to call Nutmeg.Drink() or worse, Nutmeg.PutOutFire()? If Nutmeg is a Beverage, then it should be able to serve as a beverage even when it's not being used as a decorator of the beverage. By making Nutmeg an instance of the abstract decorator, you prevent its being mistaken for an instance of the abstract class.
  • Michael Blackburn
    Michael Blackburn almost 13 years
    I kind of wish there was an (efficient) functionality like exception handling, where each level of a call stack has the opportunity to "catch" a method call and if it doesn't have a handler, pass it up. That would be hella decoupled. (and hell to debug).
  • Andrew
    Andrew about 12 years
    In Java you can dynamically handle method calls for an interface with a Dynamic Proxy. Alternatively, or when you have a class rather than an interface, cglib's Enhancer can generate implementation classes at runtime that delegate calls to a handler you provide.
  • Adams.H
    Adams.H over 10 years
    acutally , u can't add an constructor to an abstract class . so the "enforce" doesn't seem to be .
  • Dmitry
    Dmitry about 10 years
    Why then a decorator can not be a subclass of a decoree? Thus we eliminate need of having an abstract decorator which provides non-decorated implementation (simply delegates) and also in case if a decoree will be added with new methods decorator still can be used without amending it (adding delegation)!
  • Mike Nakis
    Mike Nakis about 10 years
    Huh? This did not make much sense. From the point of view of a decorator, the decoree is just an interface. So, a decorator cannot be a subclass of a decoree, because you cannot subclass an interface. You can implement an interface, and that's precisely what a decorator does, by definition.
  • Mike Nakis
    Mike Nakis about 10 years
    Now, if what you are trying to suggest here is to pick some concrete class which implements the interface, (assuming one is known,) and to subclass it to make a decorator out of it, this idea is nonsensical, because the decorator will be inheriting fields and functionality which it does not need, should not use, and has no business dealing with. I assume I did not understand your question, so perhaps you should start a new question on stack overflow and try to be as clear as possible regarding what you mean, so that it can receive a proper answer.
  • Avalanche
    Avalanche almost 10 years
    @JasonHuang: That's wrong. In Java you can add constructors to abstract classes.
  • CCNA
    CCNA over 7 years
    Conceptually the example of milk and nutmeg makes sense. However, how will the caller code to decorate the decorated twice? Say, call Milk(beverage) and then call Nutmeg(beverage), and I supposed it would still end up with a nutmeg beverage instead of a milk+nutmeg beverage..
  • tvanfosson
    tvanfosson over 7 years
    @CCNA Nutmeg(Milk(beverage)) - the decorator extends the same interface so you can compose them by passing a decorated Beverage into another decorator.
  • Will
    Will almost 7 years
    I think you might be on to something re: "ensure that the component is always reuseable", but I can't quite fully understand what you are saying. Could you elaborate?
  • Alkenrinnstet
    Alkenrinnstet almost 3 years
    This answers what the original question explicitly was not about. Why is this accepted?
  • abhigyan nayak
    abhigyan nayak over 2 years
    would be easier. if you can explain this with a help of an example. This seems difficult to understand.