私は一般的にパターンを設計することにあまり慣れておらず、Decorator を使用したことはありません。コンテキストに応じて異なる動作を持つオブジェクトが必要です。これらの動作は、異なるクラスで定義されています。Decorator がそのトリックを行うと思います。しかし、各デコレータが同じプロパティにアクセスでき、継承のように最初に子メソッドを呼び出せるようにする必要があります。だからここで私がやったこと:
abstract class Component{
/**
* Used to access last chain Decorator
*
* @var Decorator
*/
protected $this;
protected $prop1;//These properies have to be accessed in any decorators
protected $prop2;
protected $prop3;
//this method is used to share properties with the childrens
public function getAttributesReferencesArray() {
$attributes=[];
foreach($this as $attr=>&$val)
$attributes[$attr]=&$val;
return $attributes;
}
}
class Foo extends Component{
public function __construct() {
$this->prop1="initialized";
//...
}
public function method1() {//this method can be "overrided" and called here
//...
}
public function method2() {//this method call the overrided or not method1
//...
$this->this->method1();
//...
}
}
abstract class Decorator extends Component{
/**
* Used to access parent component
*
* @var Component
*/
protected $parent;
public function __construct(Component $parent) {
$attributes=$parent->getAttributesReferencesArray();
foreach($attributes as $attr=>&$val)
$this->{$attr}=&$val;
$this->parent=$parent;
$this->this=$this;
}
public function __call($method, $args) {
if(!$this->parent instanceof Decorator &&
!method_exists($this->parent, $method))
throw new Exception("Undefined method $method attempt.");
return call_user_func_array(array($this->parent, $method), $args);
}
}
class Bar extends Decorator{
//this method call the component method (I guess Decorator classical way)
public function method1(){
//...
$this->parent->method1();
$this->prop2="set in Bar";
}
}
class Baz extends Decorator{
public function method2(){//this method call the overrided or not method1
//...
$this->this->method1();
//...
}
}
これで、コンテキストに従って「継承」を「構築」できます。
//...
$obj=new Foo();
if($context->useBar())
$obj=new Bar($obj);
if($context->somethingElse())
$obj=new Baz($obj);
動作を抽象化してオブジェクトを実行します。
$obj->method1();
//...
それは私が望むことをしますが、:
- カプセル化はもうありません
- $this->親は醜い
- $this->これは醜い
あれについてどう思う?
- 別の方法でデコレータ(「子」)メソッドにアクセスするにはどうすればよいですか
- 継承されたコンテキストで保護されている場合のように、プロパティを共有するにはどうすればよいですか
- Decoratorの使い方が悪いのでしょうか?
- トリックを行うよりエレガントなパターンはありますか
- 親とこの属性は一種の車輪の再発明ですね。
現実世界の例: コーヒー マシン
abstract class CoffeeFactory{// Component
/**
* Used to access last chain Decorator
*
* @var Decorator
*/
protected $this;
/**
* Used to access user choices
*
* @var CoffeeMachine
*/
protected $coffeeMachine;
protected $water;//the water quantity in cl
protected $coffeePowder;
protected $isSpoon=FALSE;
protected $cup=[];
//this method is used to share properties with the childrens
public function getAttributesReferencesArray() {
$attributes=[];
foreach($this as $attr=>&$val)
$attributes[$attr]=&$val;
return $attributes;
}
}
class SimpleCoffeeFactory extends CoffeeFactory{//Foo
public function __construct(CoffeeMachine $coffeeMachine) {
$this->coffeeMachine=$coffeeMachine;
$this->water=$coffeeMachine->isEspresso()?10:20;
$this->coffeePowder=$coffeeMachine->isDouble()?2:1;
$this->water-=$this->coffeePowder;
$this->this=$this;
}
private function addCoffeePowder(){
$this->cup["coffeePowder"]=$this->coffeePowder;
}
private function addSpoon(){
if($this->isSpoon)
$this->cup["spoon"]=1;
}
public function isWaterHot($boilingWater){
return $this->getWaterTemperature($boilingWater)>90;
}
private function addWater() {
$boilingWater=$this->getWaterForBoiling($this->water);
while(!$this->this->isWaterHot($boilingWater))
$this->boilWater($boilingWater);
$this->cup["water"]=$boilingWater;
}
public function prepare() {
$this->addCoffeePowder();
$this->addSpoon();
}
public function getCup() {
$this->this->prepare();
$this->addWater();
return $this->cup;
}
}
abstract class Decorator extends CoffeeFactory{
/**
* Used to access parent component
*
* @var Component
*/
protected $parent;
public function __construct(Component $parent) {
$attributes=$parent->getAttributesReferencesArray();
foreach($attributes as $attr=>&$val)
$this->{$attr}=&$val;
$this->parent=$parent;
$this->this=$this;
}
public function __call($method, $args) {
if(!$this->parent instanceof Decorator &&
!method_exists($this->parent, $method))
throw new Exception("Undefined method $method attempt.");
return call_user_func_array(array($this->parent, $method), $args);
}
}
class SugarCoffeeFactory extends Decorator{
protected $sugar;
public function __construct(Component $parent) {
parent::__construct($parent);
$this->sugar=$this->coffeeMachine->howMuchSugar();
$this->water-=$this->sugar;
$this->isSpoon=TRUE;
}
public function prepare() {
$this->cup['sugar']=$this->sugar;
$this->parent->prepare();
}
}
class MilkCoffeeFactory extends Decorator{
protected $milk;
public function __construct(Component $parent) {
parent::__construct($parent);
$this->milk=$this->coffeeMachine->howMuchMilk();
$this->water-=$this->milk;
}
public function prepare() {
$this->parent->prepare();
$this->cup['milk']=$this->milk;
}
public function isWaterHot($boilingWater){
//The milk is added cold, so the more milk we have, the hotter water have to be.
return $this->getWaterTemperature($boilingWater)>90+$this->milk;
}
}
//Now we can "construct" the "inheritance" according to the coffee machine:
//...
$coffeeFactory=new SimpleCoffeeFactory($coffeeMachine);
if($coffeeMachine->wantSugar())
$coffeeFactory=new SugarCoffeeFactory($coffeeFactory);
if($coffeeMachine->wantMilk())
$coffeeFactory=new MilkCoffeeFactory($coffeeFactory);
//and get our cup with abstraction of behaviour:
$cupOfCoffee=$coffeeFactory->getCup();
//...