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

}

これまでのところ、すべて問題ありません。次に、拡張する抽象AnimalDecoratorクラスを作成しAnimalます。

abstract class AnimalDecorator extends Animal {
    protected $animal;

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

そして、ここで問題が発生します。AnimalDecoratorクラスからすべての抽象メソッドも取得することに注意してAnimalください (この例では 2 つだけですが、実際にはもっと多くのメソッドを持つことができます)。

ReptileSkinDecorator次に、拡張する具象クラスを作成しますAnimalDecorator。からの同じ 2 つの抽象メソッドもありますAnimal

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

最後に、ウサギを爬虫類の皮で飾りたいと思います。

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());

ReptileSkinDecoratorしかし、クラスに2つの抽象メソッドがあるため、これを行うことはできません. 彼らです:

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

そのため、スキンだけを再装飾するだけでなくsetSleep()setEat();メソッドも再装飾する必要があります。しかし、私はする必要はありません。

Animalすべての本の例では、クラスには常に 1 つの抽象メソッドしかありません。そしてもちろん、それは機能します。しかし、ここでは非常に単純な実際の例を作成し、Decorator パターンを使用しようとしましたが、これらの抽象メソッドをReptileSkinDecoratorクラスに実装しないと機能しません。

私の例を使用したい場合は、新しいウサギを作成し、独自のメソッドを実装する必要があることを意味しsetSleep()ますsetEat()。OK、そうしましょう。しかし、この真新しいうさぎには、Rabbit私が渡したcommont のインスタンスがありReptileSkinDecoratorます。

$reptileSkinRabbit = ReptileSkinDecorator(new Rabbit());

reptileSkinRabbit インスタンスには、独自の reptileSkinRabbit メソッドを持つ共通のウサギ インスタンスが 1 つあります。うさぎにはうさぎがいます。しかし、私はそのような可能性を持つ必要はないと思います。

Decarator パターンが正しく理解できません。このパターンの理解において、私の例の間違いを指摘してください。

ありがとうございました。

4

3 に答える 3

5

問題に合わない特定のパターンを強制的に使用しようとしているようです。デコレータは通常、皮膚(髪の毛から鱗)でやろうとしているように完全に変更するのではなく、何かを追加(髪に三つ編みを追加)で集約します。

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は本当に悪いです。このコードにはいくつかの問題があると思いますが、あなたがその考えを理解してくれることを願っています。

したがって、ウサギは動物を拡張し、それに応じてそのプロパティを設定します。次に、誰かがRabbitReptileを必要とする場合は、Rabbitを拡張する(おそらくやり過ぎ)か、Rabbitを作成してそれに応じてスキンタイプを設定することができます。

于 2012-06-02T20:45:19.060 に答える
5

あなたの質問を完全に理解したかどうかわかりません。これは私がそれを読んだ方法です:

デコレーターはまだ完成していません。それが示すように、それはまだ抽象的で不完全です:

abstract class AnimalDecorator extends Animal {
    protected $animal;

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

簡単に動作させたい場合は、すべてのメソッドも装飾する必要があるため、 から拡張するときにそれを行う必要はありませんAnimalDecorator。ここでは、多くの方法のうちの 2 つの例を示します。

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 に答える
3

Decorator パターンは、その基本クラスまたは実装されたインターフェイスで定義されたすべてのメンバーを、それが拡張するオブジェクトに委任する必要があります。つまり、ReptileSkinDecorator の setSleep、setEat、および getSkinType メソッドは、Rabbit (または爬虫類スキンで装飾するために選択したその他の Animal の子孫) の setSleep メソッドと setEat メソッドを呼び出す必要があります。

また、あなたの例では、アニマル基本クラスで定義されたメソッドの使用を変更しようとしていることに気付きました(不変にしたいスキンタイプを定義しようとしています)。オブジェクトがデコレータのスコープ外で使用可能になると、別のスキン タイプが提供されるため (定義したように)、Decorator パターンでそれを実際に行う (オブジェクトの不変プロパティを非表示にする) べきではありません。通常、デコレーターは、装飾しているオブジェクトの既存のプロパティを変更しません。

おそらく、より良い例は HibernationDecorator でしょう。基本クラスには休止状態に関連するものがないため、そのデコレータを作成して、ウサギに休止状態機能を提供できるようにすることができます。それが Decorator の目的です。基本クラスのコントラクトに準拠しながら、基本クラスまたはインターフェイス コントラクトに存在しない追加機能を提供します。

于 2012-06-02T16:27:03.387 に答える