装饰模式中有什么用处?我的例子不起作用

Gre*_*een 3 php design-patterns decorator

这本书说:

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

我有一只兔子动物.我希望我的兔子能够拥有爬行动物的皮肤.只是想用爬行动物的皮肤装饰一只普通的兔子.

我有代码.首先,我有一个抽象的类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 */
} 
Run Code Online (Sandbox Code Playgroud)

我为我的兔子创建课程:

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

到目前为止一切正常.然后我创建AnimalDecorator扩展的抽象类Animal:

abstract class AnimalDecorator extends Animal {
    protected $animal;

    public function __construct(Animal $animal) {
        $this->animal = $animal;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());
Run Code Online (Sandbox Code Playgroud)

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

abstract public function setSleep($hours);
abstract public function setEat($food);
Run Code Online (Sandbox Code Playgroud)

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

在所有书中的例子中,Animal课堂上总是只有一个抽象方法.当然,它的工作原理.但是在这里我只是制作了一个非常简单的现实生活示例,并尝试使用Decorator模式,如果没有在ReptileSkinDecorator类中实现这些抽象方法,它就无法工作.

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

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());
Run Code Online (Sandbox Code Playgroud)

我在reptileSkinRabbit实例中有一个常见的兔子实例和自己的方法,而这个实例又有自己的reptileSkinRabbit方法.我有兔子兔子.但我认为我没有这种可能性.

我不理解Decarator模式的正确方法.在我对这种模式的理解中,请你指出我的例子中的任何错误.

谢谢.

hak*_*kre 5

我不知道我是否完全理解你的问题.这就是我读它的方式:

你的装饰师尚未完成.如它所示,它仍然是抽象的和不完整的:

abstract class AnimalDecorator extends Animal {
    protected $animal;

    public function __construct(Animal $animal) {
        $this->animal = $animal;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想让它变得容易起作用,你也应该装饰所有方法,所以当你从中扩展时你不需要这样做AnimalDecorator.这里有许多方法中的两个示例:

abstract class AnimalDecorator extends Animal {

    ...

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

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


    ...
Run Code Online (Sandbox Code Playgroud)

在委派了所有方法之后,您可以像以前一样覆盖混凝土装饰器中的那些方法:

class ReptileSkinDecorator extends AnimalDecorator {
    public function getSkinColor() {
        return "reptile";
    }
}
Run Code Online (Sandbox Code Playgroud)


Bra*_*ady 5

听起来你正试图强迫使用不适合问题的特定模式.装饰者通常聚集一些额外的东西(向头发添加辫子),而不是完全改变它,就像你试图用皮肤(头发到鳞片).

一个Builder模式,(其中指定如何你想建立的对象)可以适应问题更好.在你的情况下,你想要建立一个用爬行动物皮肤建造的兔子.(我来自哪里,而不是爬行动物的皮肤,制作一只有角的兔子并称之为jackalope :) 会很有趣 :)

我认为在这里使用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";
    }
}
Run Code Online (Sandbox Code Playgroud)

免责声明:我是一个c ++人,我的PHP非常糟糕,我确信这个代码有几个问题,但我希望你能得到这个想法.

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