patterns in php

27
Patterns: - Decorator . - Composite. Diego Lewin Senior Developer at Netconcepts

Upload: diego-lewin

Post on 27-Jan-2015

121 views

Category:

Technology


1 download

DESCRIPTION

Software patterns in PHP

TRANSCRIPT

Page 1: Patterns in PHP

Patterns:

- Decorator .

- Composite.

Diego LewinSenior Developer at Netconcepts

Page 2: Patterns in PHP

Structural Patterns are Design Patterns that ease the design by identifying a simple way to realize relationships between entities.

* Adapter pattern. * Aggregate pattern. * Bridge pattern. * Composite pattern. * Decorator pattern. * Extensibility pattern. * Facade pattern. * Flyweight pattern. * Proxy pattern. * Pipes and filters. * Private class data pattern.

Structural Patterns

Page 3: Patterns in PHP

The Decorator Pattern

The decorator pattern provides us with a mechanism for adding functionality to objects at runtime, as an alternative mechanism to creating additional child classes.

Page 4: Patterns in PHP

The Problem

As an example we can use an E-comerce system, we have different modules like:

Related content (for products,categories,etc). Customer Reviews (for products).. Tag Clouds (for products,categories,etc).. MYOB integration (for products).. Custom functionality from a specific client, that we don't

want to have in the core classes.

Page 5: Patterns in PHP

The ”Classes”

Product

Customer_Review

get_custom_reviews()

Tag_Cloud

get_arr_tag_cloud()

Integration_MYOB

do_integration()

get_price()

Custom_Funtionality

is_available()

is_available()

Page 6: Patterns in PHP

Product

Customer_ReviewTag_Cloud Custom_FuntionalityIntegration_MYOB

get_custom_reviews()get_arr_tag_cloud()do_integration()

get_price()

is_available()

is_available()

How many extensions are TOO many?

Page 7: Patterns in PHP

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...

Page 8: Patterns in PHP

class Customer_Review extends Tag_Cloud {

public function get_arr_customer_review(){

.......}.........

}

class Custom_Functionality extends Customer_Review{

public function is_available(){

.......}.........

}

More Code....

Page 9: Patterns in PHP

Product

Customer_ReviewTag_Cloud Custom_FuntionalityIntegration_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

Page 10: Patterns in PHP

Customer_Review_For_Product

get_arr_custom_review()

Category Productdo_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()

Page 11: Patterns in PHP

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 !

Page 12: Patterns in PHP

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 !

Page 13: Patterns in PHP

//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$this->object_decorated

$entity_id = $this->object_decorated->id;$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$this->object_decorated

$is_available = $this->object_decorated->is_available();$is_available = $this->object_decorated->is_available(); ............ ............

return ......;}

}

Concrete Decorators

Page 14: Patterns in PHP

Customer_ReviewTag_Cloud Custom_FuntionalityIntegration_MYOB

get_custom_reviews()get_arr_tag_cloud()do_integration() is_available()

Product

get_price()

is_available()

Decorator

__call()

__get()

__set()

UML Diagram

(PHP 5 !)obj_decorated

Page 15: Patterns in PHP

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

Page 16: Patterns in PHP

Product

get_price()

is_available()

discount_brand

get_amount()

discount_category

get_amount()

discount_buy_get

get_amount()

discount_highest_price

get_amount()

discount_custom

get_amount()

Page 17: Patterns in PHP

Refactoring Diary

Composite Pattern

Page 18: Patterns in PHP

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

}

Page 19: Patterns in PHP

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...

Page 20: Patterns in PHP

But, if we have different discounts stategies for different projects, we need to updateour Product class.

Our Product class should be the same for all projects.It should be ”Open for extension, but closed for modification”

And which is the primary responsability of the Product class ? Is discount calculations one of these responsabilities? (One responsability Rule).

Open for extension, but closed for modification

Page 21: Patterns in PHP

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

Page 22: Patterns in PHP

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;

}}

Page 23: Patterns in PHP

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

Page 24: Patterns in PHP

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

}}

Page 25: Patterns in PHP

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

}

Page 26: Patterns in PHP

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]

Page 27: Patterns in PHP

But, if we have different discounts stategies for different projects, we need to updateour Product class.

Or if a client ask for a different discount strategy, and they want to apply the highest discount only, again, we need to update the Product class;

Our Product class should be the same for our all projects.

It should be ”close to modification and open to extension”