1

Most of the resources I've seen about the Decorator pattern look like the following:

interface Tea
{
    public double cost();
}
class BasicTea implements Tea
{
    public double cost() { return 1.99; }
}
abstract class TeaDecorator implements Tea
{
    private Tea base;
    public TeaDecorator(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost(); }
}
class TeaWithMilk extends TeaDecorator
{
    public TeaWithMilk(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost() + 0.30; }
}
class TeaWithSugar extends TeaDecorator
{
    public TeaWithSugar(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost() + 0.10; }
}

decoratedTea = new TeaWithSugar(new TeaWithMilk(new BasicTea()));

However, I noticed the following approach also works - decoratedTea.cost() returns the same value as above.

class Tea
{
    public double cost() { return 1.99; }
}
abstract class TeaDecorator extends Tea
{
    private Tea base;
    public TeaDecorator(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost(); }
}
class TeaWithMilk extends TeaDecorator
{
    public TeaWithMilk(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost() + 0.30; }
}
class TeaWithSugar extends TeaDecorator
{
    public TeaWithSugar(Tea tea) { this.base = tea; }
    public double cost() { return this.base.cost() + 0.10; }
}

decoratedTea = new TeaWithSugar(new TeaWithMilk(new Tea()));

Is there any problem with this method? If I would not otherwise have an interface for Tea, is it necessary to add one just to implement the Decorator pattern?

  • Abstract classes are classes that contain one or more abstract methods. When I look at your abstract class I don't see it. What gives? – candied_orange Jan 4 '18 at 20:54
  • Not sure what you mean. TeaDecorator is the abstract class, it can be extended by others. – DisgruntledGoat Jan 5 '18 at 21:12
  • I can extend any non final/sealed class. The only thing you accomplish by putting abstract on a class with no abstract methods is to ensure it can't be constructed. Is that what you wanted? You could have made every method abstract. If you wanted default behavior why are you overriding the defaults with identical behavior? Pick one way to solve this problem and be consistent. – candied_orange Jan 5 '18 at 23:11
  • No, the TeaDecorator should not be instantiated, that’s how Decorator works in every example I’ve seen (e.g. Wikipedia). Your last sentences don’t make sense to me, I’m not overriding with identical behaviour. – DisgruntledGoat Jan 6 '18 at 1:56
  • 1
    Fine, if TeaDecorator can't be constructed then explain to me when the TeaDecorator.cost() implementation of return this.base.cost(); ever gets used. It looks to me like that implementation is getting bypassed and your TeaWith* classes are using either Tea.cost() or BasicTea.cost(). Which means you have dead code in here that's just waiting to frustrate some poor maintenance programer. Try changing TeaDecorator.cost() implementation to return this.base.cost() * 2; and tell me if anything cares. If you're not going to use this implementation why not make the method abstract? – candied_orange Jan 6 '18 at 3:03
5

You can certainly implement the Decorator pattern without the language keyword interface, because you can write decorators in languages that don't distinguish at the keyword level between a class with no implemented methods, one with some un-implemented methods and one with only implemented methods. (Java and C#'s interface, abstract class and class). C++ uses class for all three.

What you can't do is talk about the Decorator pattern without the concept of the Interface of a class, because the essence of Decorator is providing something of the same Interface and an implementation that delegates (some portion) to an existing implementation of that Interface

  • “the essence of Decorator is providing something of the same Interface” — and that’s what my second example does. TeaDecorator has the same methods as Tea. I’ll update with the full code if that makes it easier to understand. – DisgruntledGoat Jan 5 '18 at 21:08
  • That's my point. Forget there is a keyword interface. The public members of a class are an interface – Caleth Jan 6 '18 at 8:53
  • OK thanks. It wasn’t clear to me whether you agreed my example was correct. – DisgruntledGoat Jan 6 '18 at 13:15
0

A definition of the Decorator pattern (my emphasis) is:

[...] decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. https://en.m.wikipedia.org/wiki/Decorator_pattern

Your second example uses inheritance; so each instance of TeaWithMilk would have the same behaviour.

It is certainly possible to implement the Decorator pattern without interfaces.
For example a JavaScript version:

class Tea
{
    cost() { return 1.99; }
}

class WithMilk
{
    constructor(tea)
    {
        this.tea = tea;
    }

    cost()
    {
        return this.tea.cost() + 0.50;
    }
}
  • What do you mean by “each instance of TeaWithMilk would have the same behaviour”? If I pass in a different type of tea then it will have different behaviour. Also I’m not sure that your JS version works well, because every class you make (WithSugar etc) needs to implement every method from Tea, even if just calling through to the base class. By extending a decorator you don’t need to. – DisgruntledGoat Jan 5 '18 at 21:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.