3

书上说:

装饰器模式可用于扩展(装饰)某个对象的功能

我有一只兔子动物。例如,我希望我的兔子有爬行动物的皮肤。只想用爬行动物的皮肤装饰一只普通的兔子。

我有代码。首先,我有Animal任何动物共有的所有抽象类:

abstract class Animal {
    abstract public function setSleep($hours);
    abstract public function setEat($food);
    abstract public function getSkinType();

    /* and more methods which for sure will be implemented in any concrete animal */
} 

我为我的兔子创建课程:

class Rabbit extends Animal {
    private $rest;
    private $stomach;
    private $skinType = "hair";

    public function setSleep($hours) {
        $this->rest    = $hours;
    }

    public function setFood($food) {
        $this->stomach = $food;
    }

    public function getSkinType() {
        return $this->$skinType;
    }

}

到目前为止一切正常。然后我创建扩展的抽象AnimalDecoratorAnimal

abstract class AnimalDecorator extends Animal {
    protected $animal;

    public function __construct(Animal $animal) {
        $this->animal = $animal;
    }
}

问题来了。注意AnimalDecorator也从类中获取所有抽象方法Animal(在这个例子中只有两个,但实际上可以有更多)。

然后我创建ReptileSkinDecorator扩展的具体类AnimalDecorator。它还具有相同的两个抽象方法Animal

class ReptileSkinDecorator extends AnimalDecorator {
    public function getSkinColor() {
        $skin = $this->animal->getSkinType();
        $skin = "reptile";
        return $skin;
    }
}

最后我想用爬行动物的皮肤装饰我的兔子:

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());

但我不能这样做,因为我在ReptileSkinDecorator课堂上有两个抽象方法。他们是:

abstract public function setSleep($hours);
abstract public function setEat($food);

所以,我不仅要重新装饰皮肤,还必须重新装饰setSleep()setEat();方法。但我不需要。

在所有书籍示例中,类中始终只有一个抽象方法Animal。当然它会起作用。但是在这里我只是做了一个非常简单的现实生活示例并尝试使用装饰器模式,如果不在类中实现这些抽象方法,它就无法工作ReptileSkinDecorator

这意味着如果我想使用我的示例,我必须创建一个全新的兔子并为其实现自己的方法setSleep()setEat()方法。好吧,就这样吧。但是后来这个全新的兔子有Rabbit我传递给的 commont 实例ReptileSkinDecorator

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());

我有一个普通的兔子实例,它在 reptileSkinRabbit 实例中有自己的方法,而它又具有自己的 reptileSkinRabbit 方法。我有兔子在兔子。但我想我不必有这种可能。

我不正确地理解 Decarator 模式。在我对这种模式的理解中,请您指出我的示例中的任何错误。

谢谢你。

4

3 回答 3

5

我不知道我是否完全理解你的问题。我是这样读的:

您的装饰器尚未完成。正如它所显示的,它仍然是抽象和不完整的:

abstract class AnimalDecorator extends Animal {
    protected $animal;

    public function __construct(Animal $animal) {
        $this->animal = $animal;
    }
}

如果你想让它轻松工作,你也应该装饰所有方法,所以当你从AnimalDecorator. 这里是众多方法中的两种示例:

abstract class AnimalDecorator extends Animal {

    ...

    public function getSkinColor() {
        return $this->animal->getSkinColor();
    }

    public function setSleep($hours) {
        $this->animal->setSleep($hours);
    }


    ...

委派所有方法后,您可以像已经做的那样在具体装饰器中覆盖您想要的方法:

class ReptileSkinDecorator extends AnimalDecorator {
    public function getSkinColor() {
        return "reptile";
    }
}
于 2012-06-02T16:28:42.830 回答
5

听起来您正试图强制使用不适合问题的特定模式。装饰者通常会聚合一些额外的东西(在头发上添加辫子),而不是完全改变它,就像你试图对皮肤做的那样(头发到鳞片)。

Builder 模式(您可以在其中指定您希望如何构建对象)可能更适合该问题。在您的情况下,您想构建一个用爬行动物皮肤构建的兔子。(我来自哪里,而不是爬行动物的皮肤,做一只有角的兔子并称之为鹿角兔会很有趣:)

我认为在这里使用 Builder(或任何模式)实际上可能是矫枉过正。对于这个特定问题,您绝对必须使用模式吗?不如只定义如下代码并暂时将模式排除在外:

class Animal {
    private $rest;
    private $stomach;
    private $skinType;

    public function setSleep($hours) {
        $this->rest    = $hours;
    }

    public function setFood($food) {
        $this->stomach = $food;
    }

    public function setSkinType($skin) {
        $this->skinType = $skin;
    }

    // define the other getters too

    public function getSkinType() {
        return $this->$skinType;
    }
}

class Rabbit extends Animal {
    public function __construct() {
        $this->rest = "rabbitRest";        // put whatever a rabbit rest is
        $this->stomach = "rabbitStomach";  // put whatever a rabbit stomach is
        $this->skinType = "hair";
    }
}

免责声明:我是一个 c++ 人,我的 php 真的很糟糕,我确定这段代码有几个问题,但我希望你明白。

所以 Rabbit 只会扩展 Animal 并相应地设置它的属性。然后,如果有人想要 RabbitReptile,他们可以扩展 Rabbit(可能是矫枉过正)或者只制作 Rabbit 并相应地设置皮肤类型。

于 2012-06-02T20:45:19.060 回答
3

装饰器模式必须将其基类或实现的接口中定义的所有成员委托给它正在扩展的对象。换句话说,ReptileSkinDecorator 的 setSleep、setEat 和 getSkinType 方法必须调用 Rabbit(或您选择用爬行动物皮肤装饰的任何其他动物后代)的 setSleep 和 setEat 方法。

另外,我刚刚注意到,在您的示例中,您正在尝试更改 Animal 基类中定义的方法的使用(尝试定义皮肤类型,我假设您希望它是不可变的)。您不应该使用装饰器模式真正做到这一点(隐藏对象的不可变属性),因为如果该对象在装饰器范围之外可用,它将提供不同的皮肤类型(如您所定义的那样)。装饰器通常不会更改它们正在装饰的对象的现有属性。

也许一个更好的例子是 HibernationDecorator。由于您的基类与休眠没有任何关系,您可能希望制作该装饰器,以便您可以为您的兔子提供休眠功能。这就是装饰器的意义所在——提供基类或接口契约中不存在的附加功能,同时仍遵守基类的契约。

于 2012-06-02T16:27:03.387 回答