1

I have seen in many UML class diagrams related to the decorator pattern that the abstract decorator contains both an abstract component and at the same time extends the abstract class component.

For instance, in the second example related to a coffee making scenario from the following link:

https://en.wikipedia.org/wiki/Decorator_pattern#Examples

the class CoffeeDecorator extends both the abstract component Coffee and contains a field of type Coffee. Why is that? Is this just a case or should we always structure how "decorator systems" like that, and why?

I thought it should only be necessary for the CoffeeDecorator to contain a component that it wants to decorate, since CoffeeDecorator is not actually a Coffee.

nbro
  • 15,395
  • 32
  • 113
  • 196

4 Answers4

2

A decorator/ wrapper can be used whereever the wrapped object can be used ("is a"). That's why it has to extend/ implement the class/ interface.

A decorator/ wrapper also has a reference to the instance it wants to decorate/ wrap ("has a").

As the linked example in your question shows, you can then write:

 Coffee coffee = new WithMilk(new SimpleCoffee());

where WithMilk is a decorator.

Puce
  • 37,247
  • 13
  • 80
  • 152
1

Your question seems a bit circular to me...implementing an interface and having an instance of that interface that is "decorated" is the definition of the decorator pattern.

So your question is a bit like asking "Why does the decorator pattern use the decorator pattern?"

If there isn't a common interface between the thing you're wrapping and yourself, it's just a has-a relationship, or, if you're adapting the interface of the thing you're wrapping to some other interface, it might be the adapter pattern.

I think your real question is "Why is the decorator pattern useful? What makes it useful to implement the same interface as something you wrap?"

The Decorator Pattern Enables Variations on the Same Behavior

The greatest value of the decorator pattern comes when you have some system that must handle several subtly different things, but wants to not have to know about the complexity, and be able treat them all the same way. This is generally what we get from interfaces, of course. But in some cases, numerous implementations of the same interface are not the best solution...in the classic example, suppose you tried to write a separate class for every type of coffee that Starbucks serves.

You'll start out with the basics, but gradually find yourself either needing to write classes like VentiMochaChaiWhipWithCinnamonOnTop. Each time a new flavor ingredient is added, you'd need to write dozens of new classes, and each time one is removed, dozens of classes would become obsolete.

With the decorator pattern, on the other hand, you can have what feels like a separate class for each drink, and enable large portions of the software system to not have to deal with the variety of drink types; they can treat each drink the same (no matter how many add-ons it has) through the Drink interface.

You just make a new Chai() when the person at the checkout hits the Chai button, and because the other attributes can be added to an arbitrary drink, you just wrap it with new Venti(drink), new Mocha(drink), new Whip(drink), new WithCinnamon(drink), etc. as the customer requests add-ons and the person at the register hits the buttons.

Limitations of the Decorator Pattern

The pattern has its limitations, since it's a bit harder to do introspection about what exact drink you have than, say, if you just used a set of booleans for each flavoring. I'm pretty sure it's not something that would actually be useful for keeping track of drink types in designing a cash register for Starbucks, because it wouldn't be very good at handling "Oops, I meant blackberry flavor, not raspberry"...supporting that in the decorator pattern would require introspection through several nested Drink decorators to find the Rasberry decorator that needs to be removed.

It also isn't very good at handling things that have complicated inter-relationships...if the whipped topping needs to know what base drink type it's associated with to know whether it should add $.25 to the price or not, then the decorator pattern won't be very useful: the decorator pattern is for cases where the various decorations don't have to know about what exact implementation they're wrapping. When we can use it, it helps reduce coupling.

What I Really Find Useful

I think we use the drink example for explaining the decorator pattern mostly because it's very easy to talk about the potentially exponential savings in the number of classes that need to be created, while many real examples of where it's useful to decorate something don't actually use that property of the decorator pattern.

The cases that I can think of where something close to the pattern has been used at our company have been cases where we wanted to add some logging to a process. For example, by decorating an Eclipse IProgressMonitor, we can detect each time the displayed task or subtask name changes, and log how long each step of the process takes, so we know what steps are taking the most time.

Doing this as a decorator gives us the ability to access all that logging information without having to change much of our existing code (just the start-up of the job to be monitored), as it all knows how to handle an IProgressMonitor already, and we don't have to know what the progress monitor we wrap does internally either: we don't have to provide an alternative implementation, just wrap an existing implementation withe the additional behavior we want.

Theodore Murdock
  • 1,538
  • 1
  • 13
  • 28
  • My question is **not**: "Why is the decorator pattern useful". I know why it is useful: associate dynamically behaviour to objects. I was confusing about the fact: why should the wrapper class be strictly compatible with the component? Thanks for the additional explanations about the limitations of the pattern. – nbro Dec 03 '15 at 20:43
0

Decorator and decorated class implement same interface. This allows decorator class to add extra functionality to every methods(declared in interface) of decorated class. This is what wrapping/decorating means.

public class SimpleCoffee extends Coffee {
    @Override
    public double getCost() {
        return 1;
    }
    @Override
    public String getIngredients() {
        return "Coffee";
    }
}

public abstract class CoffeeDecorator extends Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    public double getCost() { // Implementing methods of the abstract class
        //you can add  extra functionality here.
        return decoratedCoffee.getCost();
    }

    public String getIngredients() {
        //you can add  extra functionality here.
        return decoratedCoffee.getIngredients();
    }
}

Decorator class can be decorated by bigger decorator.

public class BiggerDecorator extends Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee c) {
        this.decoratedCoffee = c;
    }

    public double getCost() { // Implementing methods of the abstract class

        return decoratedCoffee.getCost();
    }

    public String getIngredients() {

        return decoratedCoffee.getIngredients();
    }
}

You now can do following. Java IO classes is in this structure.

Coffee coffeeDecorator=new CoffeeDecorator(new Coffee);
Coffee biggerDecorator=new BiggerDecorator(coffeeDecorator);

For the sake of polymorphism , decorator class has to implement that interface.

Community
  • 1
  • 1
Mustafa ASAN
  • 3,747
  • 2
  • 23
  • 34
0

The answer is mentioned in the GOF book in the decorator pattern introduction.

This is the quote:

"The decorator conforms to the interface of the component it decorates so that its presence is transparent to the component's clients. Transparency lets you nest decorators recursively, thereby allowing an unlimited number of added responsibilities".

So, I think the client need not worry about two different interfaces, one for decorator and other for component. The other reason is what Puce's answer mentions.

juagicre
  • 1,065
  • 30
  • 42