开发者

Decorator pattern: Why do we need an abstract decorator?

开发者 https://www.devze.com 2022-12-15 20:30 出处:网络
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 i

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开发者_StackOverflow中文版 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?


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.


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.


(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! ^_^


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.


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.


actually i also sometimes leave out this 'middle-man' abstraction (if you don't have many decorator combinations). it decouples more but also adds complexity. in my view the main idea behind decorating is wrapping interfaces inside their own implementations.


In the case of the Decorator pattern, inheritance is being used for type matching. This is not the typical reason for sub-classing. The normal reason for sub-classing is to inherit behavior

To make this distinction clear, it makes sense to sub-class Beverage with CondimentDecorator because the CondimentDecorator class makes the distinction between a drink implementation (like DarkRoast) and condiment (like Mocha) clear. Consider for example you were tasked to come up with a menu for StarBuzz just by looking at the code. You would immediately know which are "base" beverages and which condiments by looking as the base class. DarkRoast's base class is Beverage. Mocha's base class is CondimentDecorator.

Still, I think it might make more sense in Java to implement the Beverage abstract class as an interface instead. According to the book, the code didn't use this approach because StarBuzz already had an abstract Beverage class (p 93) and an abstract base component is the historical approach to implementing the pattern.


The BaseDecorator (CondimentDecorator) class enforce the ConcreteDecortor (Milk) to have a base abtract class (Brevrage) in input in the constructor.

If the BaseDecorator class does not exist, it does not force you to implement a decorator that require a BaseAbstractClass in input. It is the goal of the Decorator Pattern to enforce input and ouput of classes.

(I think you take your example from Head First Design Pattern, and in their example, the BaseDecorator class do not have this contructor).


maybe years too late but here is the point with the gof statement of single responsibiltiy.

suppose the abstract decorator also have additional methods not own by the component (abstract) there by the decarator have multiple responsibilty.

this ensure that the component is always reuseable for it should implements all abstract / interface of the component but must not implements the decorator additional responsibilty.

any program that use the component(abstract) can ensure the component works accordingly in the utilization wise(into running object) and concrtete creation of the component(abstract). thus this will ensure program integrity and ease of development and expandsion of application/program.

thus decorator will leave those alove and then as though stand as his own object to be use in any manner as possible include manners similar to above. eg. decorator being decorated.(just an idea)

great isnt it? (clap)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号