oop - full inheritance behaviour with Decorator in php -


i'm not used design pattern generally, , never used decorator. want object can have different behaviour according context. these behaviours defined in different classes. guess decorator trick. need each decorator can access same properties, , call children methods first, inheritance. here i've done:

abstract class component{      /**      * used access last chain decorator      *      * @var decorator      */     protected $this;      protected $prop1;//these properies have accessed in decorators      protected $prop2;      protected $prop3;      //this method used share properties childrens     public function getattributesreferencesarray() {         $attributes=[];         foreach($this $attr=>&$val)                 $attributes[$attr]=&$val;         return $attributes;     }  }  class foo extends component{      public function __construct() {         $this->prop1="initialized";         //...     }      public function method1() {//this method can "overrided" , called here         //...     }      public function method2() {//this method call overrided or not method1         //...         $this->this->method1();         //...     }  }  abstract class decorator extends component{      /**      * used access parent component      *      * @var component      */     protected $parent;      public function __construct(component $parent) {         $attributes=$parent->getattributesreferencesarray();         foreach($attributes $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 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 overrided or not method1         //...         $this->this->method1();         //...     }  } 

now can "construct" "inheritance" according context:

//... $obj=new foo(); if($context->usebar())         $obj=new bar($obj); if($context->somethingelse())         $obj=new baz($obj); 

and run object abstraction of behaviour:

$obj->method1(); //... 

it want, but:

  • there isn't anymore encapsulation
  • $this->parent ugly
  • $this->this ugly

what think that?

  • how can access decorator ("children") method way
  • how can share properties if protected in inherited context
  • is bad usage of decorator?
  • is there more elegant pattern trick
  • parent , attributes kind of reinventing wheel isn't it?

a real world example: coffee machine

abstract class coffeefactory{// component      /**      * used access last chain decorator      *      * @var decorator      */     protected $this;      /**      * used access user choices      *      * @var coffeemachine      */     protected $coffeemachine;      protected $water;//the water quantity in cl      protected $coffeepowder;      protected $isspoon=false;      protected $cup=[];      //this method used share properties childrens     public function getattributesreferencesarray() {         $attributes=[];         foreach($this $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 access parent component      *      * @var component      */     protected $parent;      public function __construct(component $parent) {         $attributes=$parent->getattributesreferencesarray();         foreach($attributes $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 added cold, more milk have, hotter water have be.         return $this->getwatertemperature($boilingwater)>90+$this->milk;     }  }  //now can "construct" "inheritance" according coffee machine:  //... $coffeefactory=new simplecoffeefactory($coffeemachine); if($coffeemachine->wantsugar())         $coffeefactory=new sugarcoffeefactory($coffeefactory); if($coffeemachine->wantmilk())         $coffeefactory=new milkcoffeefactory($coffeefactory);  //and our cup abstraction of behaviour:  $cupofcoffee=$coffeefactory->getcup(); //... 

the decorator pattern not made internal changes in base class (you call 1 parent). doing bad usage of pattern. decorators should change output of functions instead of playing variables.

one solution define getters , setters protected variables , call them decorator.

another solution prefer , splitting behaviour dependent on context , base class:

class component {     protected $behaviour;     function __construct() {         $this->behaviour = new standardbehaviour();     }      function method1() {         $this->prop2 = $this->behaviour->getprop2value();     }     function setbehaviour(behaviour $behaviour) {         $this->behaviour = $behaviour;     } }  abstract class behaviour {     abstract function getprop2value(); }  class standardbehaviour extends behaviour {     function getprop2value() {         return 'set bahaviour ';     } }  class barbehaviour extends standardbehaviour {     function getprop2value() {         return parent::getprop2value().' bar';     } }  class bazbehaviour extends barbehaviour {     function getprop2value() {         return 'set in baz';     } } 

now can use this:

$obj=new foo(); if($context->usebar())     $obj->setbehaviour(new barbehaviour); if($context->somethingelse())     $obj->setbehaviour(new bazbehaviour); 

i hope answers question!

edit after comments

i see point behaviours replace each other instead of chaining. indeed typical problem decorator class. shouldn't change original class in decorator class. decorator class 'decorates' output of original. below typical example of how decorator pattern used in real world scenario mentioned:

interface icoffeefactory {     public function producecoffee(); }  class simplecoffeefactory implements icoffeefactory{     protected $water;//the water quantity in cl      public function __construct() {         $this->water=20;     }      protected function addcoffeepowder($cup){         $cup["coffeepowder"]=1;         return $cup;     }      protected function addwater($cup) {         $cup["water"]=$this->water;         return $cup;     }      public function producecoffee() {         $cup = array();         $cup = $this->addcoffeepowder($cup);         $cup = $this->addspoon($cup);         $cup = $this->addwater($cup);         return $cup;     }  }  class espressocoffeefactory extends simplecoffeefactory {     public function __construct() {         $this->water=5;     }      protected function addcoffeepowder($cup){         $cup["coffeepowder"]=3;         return $cup;     } }  abstract class decorator implements icoffeefactory {     function __construct(icoffeefactory $machine) }  class sugarcoffee extends decorator{     public function producecoffee() {         $cup = $this->factory->producecoffee();         if ($cup['water'] > 0)             $cup['water'] -= 1;          $cup['spoon']  = true;         $cup['sugar'] += 1;         return $cup;     } }  class milkcoffee extends decorator{     protected function producecoffee() {         $cup = $this->factory->producecoffee();         $cup['milk'] = 5;         return $cup;     } }  //now can "construct" "inheritance" according coffee machine:  //... $coffee=new simplecoffeefactory(); if($coffeemachine->wantsugar())         $coffee=new sugarcoffee($coffee); if($coffeemachine->wantmilk())         $coffee=new milkcoffee($coffee);  //and our cup abstraction of behaviour:  $cupofcoffee=$coffee->producecoffee(); //... 

Comments

Popular posts from this blog

plot - Remove Objects from Legend When You Have Also Used Fit, Matlab -

java - Why does my date parsing return a weird date? -

Need help in packaging app using TideSDK on Windows -