6. Customer_Review Tag_Cloud Custom_Funtionality Integration_MYOB get_custom_reviews() get_arr_tag_cloud() do_integration() get_price() is_available() is_available() How many extensions are TOO many? Product
7. class Product { public function get_price(); public function is_available(); } class Integration_MYOB extends Product { public function do_integration() { ....... } ......... } class Tag_Cloud extends Integration_MYOB { public function get_arr_tag_cloud() { ....... } ......... } Some Code...
8. class Customer_Review extends Tag_Cloud { public function get_arr_customer_review() { ....... } ......... } class Custom_Functionality extends Customer_Review { public function is_available() { ....... } ......... } More Code....
9. Product Customer_Review Tag_Cloud Custom_Funtionality Integration_MYOB get_custom_reviews() get_arr_tag_cloud() do_integration() do_integration() is_available() is_available() Category But, not all the projects need the same functionality... I need to change my Code.., Class EXPLOSION
10. Customer_Review_For_Product get_arr_custom_review() Category Product do_integration() is_available() Customer_Review_For_Category get_arr_custom_review() Tag_Cloud_For_Product get_arr_tag_cloud() Tag_Cloud_For_Category get_arr_tag_cloud() Even, there is more... I should reuse my extended classes !! get_arr_product()
11. What, if we ”extend” the class in a 'run time' dynamically. Magic !! Example : /* *instantiation * we pass in the constructor, the object that we want to extend.. (decorate) */ $obj_decorated_product = new Customer_Review(new Custom_Functionality( new Product)); /* * calling 'decorated' methods, the final reslut is the same as we extend the classes */ $arr_custom_review = $obj_decorated_product->get_arr_custom_review(); $is_available = $obj_decorated_product->is_available(); The Decorator comes to save us !
12. Example 2: //instantiation, with 4 decoratros $obj_decorated_product = new Integration_MYOB( new Tag_Cloud( new Customer_Review(new Custom_Functionality( new Product)))); //calling 'decorated' methods $arr_custom_review = $obj_decorated_product->get_arr_custom_review(); $is_available = $obj_decorated_product->is_available(); $arr_tag_cloud = $obj_decorated_product->get_arr_tag_cloud(); Example 3: //instantiation, 'decorating' a product object and a category object with the same 'decorators' $obj_decorated_product = new Tag_Cloud( new Customer_Review( new Product)); $obj_decorated_category = new Tag_Cloud( new Customer_Review( new Category)); More Examples !
13. //All the decorators have to extend the Abstract_Decorator class class Customer_Review_Decorator extends Abstract_Decorator { public function get_arr_customer_review() { //insted of using parent:: , we need to use $this->object_decorated $entity_id = $this->object_decorated->id; ............ ............ return ......; } } class Custom_Functionality_Decorator extends Abstract_Decorator { public function is_available() { //insted of using parent:: , we need to use $this->object_decorated $is_available = $this->object_decorated->is_available(); ............ ............ return ......; } } Concrete Decorators
15. abstract class Abstract_Decorator { /** * It is the object that we are decorating * and we must set it in the contructor */ protected $obj_decorated; public function __construct( $obj_decorated ) { $this->obj_decorated = $obj_decorated; } /** * If a function is not found in this class the call * came here and we forward to the $this->obj_decorated object */ protected function __call($method,$arguments) { return call_user_func_array(array(&$this->obj_decorated, $method),$arguments); } /** * If a parameter that we are trying to read is not found in this class the call * came here and we forward to the $this->obj_decorated object */ protected function __get($property_name) { return $this->obj_decorated->$property_name; } /** * If a parameter that we are tring to write is not found in this class the call * came here and we forward to the $this->obj_decorated object */ protected function __set($property_name, $property_value) { return $this->obj_decorated->$property_name = $property_value; }
18. Discount calculations. Class Product { public function get_price() { $price =... //now we apply the discounts $obj_discount_brand = new Discount_Brand($this->id,$qty); $price -= $obj_discount_brand->get_brand_discount(); $obj_discount_category = new Discount_Category(); $obj_discount_category->set_product_id($this->id); $obj_discount_category->set_qty($qty); $price -= $obj_discount_category->get_discount(); $obj_discount_highest_price = new Discount_Highest_Price($this->id,$qty); $price -= $obj_discount_category->get_amount(); $obj_discount_buy_get = new Discount_Buy_Get; $obj_discount_custom1 = new Discount_Custom1; $obj_discount_custom1 = new Discount_Customer_Big_Clients_EEUU; $obj_discount_custom1 = new Discount_Customer_EEUU; $obj_discount_custom1 = new Discount_Customer_NZ; return $price; } }
19. Class Product { public function get_price() { $price =... //now we apply the discounts $obj_discount_brand = new Discount_Brand(); $obj_discount_brand->set_product_id($this->id) $obj_discount_brand->set_qty($this->qty) $price -= $obj_discount_brand->get_amount(); $obj_discount_category = new Discount_Category(); $obj_discount_category->set_product_id($this->id) $obj_discount_category->set_qty($this->qty) $price -= $obj_discount_category->get_amount(); $obj_discount_highest_price = new Discount_Highest_Price(); ............ ............... ............. return $price; } } A Commun Interface for the Discount classes and..what if the discounts have a commun interface...
20.
21. Class Product { public function get_price() { $price =...; $obj_discount = new Discount_Composite; $obj_discount->set_product_id($product->id); $obj_discount->set_qty($product->qty); $price -=$obj_discount->get_amount(); return $price } } We are still using the same interface for the discount class, that we were using for each strategy: ->set_product_id($id) ->set_qty(qty) ->get_amount() The new 'stable' Product class Decoupling the Discount from the Product class
22. Decoupling the Discount from the Product class class Discount { public function __constructor( ) { $this->_arr_obj_discount_strategy array(new Discount_ Brand,new Discount_Category, new...) } public function set_id ($id) { foreach($this->_arr_obj_discount_strategy as &$obj_discount_strategy) { $obj_discount_strategy->set_id ($id); } } public function set_qty ($qty) { foreach($this->_arr_obj_discount_strategy as &$obj_discount_strategy) { $obj_discount_strategy->set_qty ($qty); } } public function get_amount() { foreach($this->_arr_obj_discount_strategy as $obj_discount_strategy) { $amount += $obj_discount_strategy-> get_amount(); } return $amount; } }
23. class Discount_Composite { public function add_strategy($obj_strategy) { $this->_arr_obj_discount_strategy[]= $obj_strategy; } public function set_id($id) { foreach($this->_arr_obj_discount_strategy as &$obj_discount_strategy) { $obj_discount_strategy->set_id ($id); } } public function set_qty($qty) { foreach($this->_arr_obj_discount_strategy as &$obj_discount_strategy) { $obj_discount_strategy->set_qty ($qty); } } public function get_amount() { foreach($this->_arr_obj_discount_strategy as $obj_discount_strategy) { $amount += $obj_discount_strategy-> get_amount(); } return $amount; } } Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. [GoF, p163] The discount composite class
24. Class Product_Controller { public function product() { .................... .................... $obj_discount = new Discount_Composite $obj_discount->add_strategy(new Discount_Brand); $obj_discount->add_strategy(new Discount_Category); .................... .................... $obj_product->set_obj_discount($obj_discount); } } Class Product_Controller { public function product() { .................... .................... $obj_discount = new Discount_Brand; .................... .................... $obj_product->set_obj_discount($obj_discount); } }
25. Class Product_Controller { public function product() { .................... .................... $obj_discount_composite = new Discount_Composite $obj_discount_composite->add_strategy( new Discount_Brand); $obj_discount_composite->add_strategy( newDiscount_Category); .................... .................... if(is_object($obj_customer)) { $obj_discount_composite_customer = new Discount_Composite $obj_discount_composite_customer->add_strategy('Discount_Seniors'); $obj_discount_composite_customer->add_strategy('Discount_Students'); } $obj_discount_composite->add_strategy($obj_discount_composite_customer); $obj_discount_composite->get_amount($obj_product ->get_id(),$obj_product ->get_qty()); } }
26. Discount_Interface set_product_id() set_qty() get_amount() Discount_Composite add_strategy() remove_strategy() Concrete_Discount Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. [GoF, p163]