The Decorator Design Pattern

2019-03-20 18:33发布

I am just starting to learn design patterns and I have two questions related to the Decorator...

I was wondering why the decorator pattern suggests that the decorator implement all public methods of the component of which it decorates?

Can't the decorator class just be used to provide the additional behaviors, and then the concrete component (which is passed into it) just be used to call everything else?

And secondly, what if the concrete component you want to decorate doesn't have a base class which the abstract decorator can also derive from?

Thanks in advance!

2条回答
家丑人穷心不美
2楼-- · 2019-03-20 18:54

I think you have have misunderstood Decorator. You're thinking of a simple case of extending a concrete class with additional functionality. In this case, yes in most OO languages the derived class can simply allow its superclass to handle any unimplemented methods.

class Base {

  function foo() {
    return "foo";
  }

  function bar() {
    return "bar";
  }

}

// Not what we think of as a Decorator,
// really just a subclass.
class Decorator extends Base {

  // foo() inherits behavior from parent Base class 

  function bar() {
    return parent::bar() . "!"; // add something
  }

}

A Decorator class does not extend the base class of its "decorated" class. It is a different type, which has a member object of the decorated class. Thus it must implement the same interface, if only to call the respective method of the decorated object.

class Decorator { // extends nothing
  protected $base;

  function __construct(Base $base) {
    $this->base = $base;
  }

  function foo() {
    return $base->foo();
  }

  function bar() {
    return $base->foo() . "!"; // add something
  }

}

It might be worthwhile to define an interface (if your language supports such a thing) for both the decorated class and the Decorator class. That way you can check at compile time that the Decorator implements the same interface.

interface IBase {
  function foo();
  function bar();
}

class Base implements IBase {
  . . .
}

class Decorator implements IBase {
  . . .
}

Re: @Yossi Dahan's comment: I see the ambiguity in the wikipedia article, but if you read carefully it does say that the component being decorated is a field in the decorator object, and that the component is passed as an argument to the decorator constructor. This is different from inheritance.

Though the wikipedia article does say the decorator inherits from the component, you should think of this as implementing an interface, as I showed in the PHP example above. The decorator still has to proxy for the component object, which it wouldn't if it had inherited. This allows the decorator to decorate an object of any class that implements that interface.

Here are some excerpts from "Design Patterns: Elements of Reusable Object-Oriented Software" by Gamma, Helm, Johnson, and Vlissides:

Decorator

Intent

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Motivation

... A decorator conforms to the interface of the component it decorates so that its presence is transparent to the component's clients.

Participants

  • Decorator maintains a reference to a Component object and defines an interface that conforms to Component's interface.
查看更多
不美不萌又怎样
3楼-- · 2019-03-20 19:11

I was wondering why the decorator pattern suggests that the decorator implement all public methods of the component of which it decorates?

A decorator should be a drop in replacement for the component it decorates, with extra functionality (the "decoration"). This can only happen if it completely implements the component's interface.

Can't the decorator class just be used to provide the additional behaviors, and then the concrete component (which is passed into it) just be used to call everything else?

That makes assumptions on how the decorator is implemented. You can't be sure that the entire public interface of the component it decorates is being passed through directly. It's possible that the decorator overrides certain methods in its implementation.

And secondly, what if the concrete component you want to decorate doesn't have a base class which the abstract decorator can also derive from?

Decorators generally inherit the component's class they are decorating, not the component's base.

查看更多
登录 后发表回答