Decorator_裝飾模式的定義:
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式比生成子類更加靈活。
應用Decorator_裝飾模式解決問題的思路:
考慮這樣一個應用場景,即靈活的實現獎金的計算。在實際的應用中,很多公司對於銷售人員的獎金計算方式五花本門,並且經常變動。是非常複雜的。
首先是獎金的分類,對於個人大致有個人當月業務獎金、個人累計獎金、個人業務增長獎金、及時回款獎金等等。
其次是計算獎金的金額,又有這麼幾個基數,銷售額,銷售毛利,實際回款,業務成本,獎金基數等。
看了上面獎金的計算問題,大家是不是覺得特別的頭疼,這裏我們只討論設計模式,並不真正要去實現整體的業務。簡化一下,演示用的獎金計算體系如下:
每個人當月業務獎金 = 當月銷售額 * 3%;
每個人累計獎金 = 累計銷售額 * 0.1%;
團隊獎金 = 團隊總銷售額 * 1%;
根據裝飾模式的定義來看,要用這個模式解決此業務問題,我們需要如下的結構:
Component:組件對象接口,可以給這些對象動態的添加職責
ConcreteComponent:具體的組件對象,實現組建對象接口,通常就是被裝飾器裝飾的原始對象,也就是可以給這個對象添加職責。
Decorator:所有裝飾器類的抽象父類,需要定義一個與組件接口一致的接口,並持有一個Component對象, 其實就是持有一個被裝飾的對象.
ConcreteDecorator:實際的裝飾器對象,實現具體要向被裝飾對象添加的功能。
注意:這個被裝飾的對象不一定是最原始的那個對象了,也可能是被其他裝飾器裝飾過後的裝飾器對象,反正都是實現的同一個接口,也就是同一類型。
001 |
<?php |
002 |
/** |
003 |
*
Decorator_裝飾模式_PHP語言描述 |
004 |
*/ |
005 |
006 |
/** |
007 |
*
計算獎金的組建接口 |
008 |
*/ |
009 |
abstract class Component{ |
010 |
/** |
011 |
*
計算某人在某階段內的獎金,有些參數在演示中並不會使用 |
012 |
*
但在實際業務實現上會用的,爲了表示這是個具體的業務方法,因此這些參數被保留了。 |
013 |
*
@param $user |
014 |
*
@param $begin |
015 |
*
@param $end |
016 |
*
<a href="http://my.oschina.net/u/556800" class="referer" target="_blank">@return</a> int 某人在某段時間內的獎金 |
017 |
*/ |
018 |
public abstract function calcPrize( $user , $begin , $end ); |
019 |
} |
020 |
021 |
/** |
022 |
*
基本的實現計算獎金的類,也是被裝飾器裝飾的對象 |
023 |
*/ |
024 |
class ConcreteComponent extends Component{ |
025 |
public function calcPrize( $user , $begin , $end ){ |
026 |
//只是一個默認的實現,默認沒有獎金 |
027 |
return 0; |
028 |
} |
029 |
} |
030 |
031 |
032 |
class TempDB{ |
033 |
|
034 |
static $SaleMoneyList = array ( 'zs' =>10000, 'ls' =>20000, 'ww' =>30000); |
035 |
|
036 |
public static function getMonthSaleMoney( $user ){ |
037 |
return TempDB:: $SaleMoneyList [ $user ]; |
038 |
} |
039 |
} |
040 |
041 |
/** |
042 |
*
裝飾器的接口,需要和被裝飾的對象實現同樣的接口 |
043 |
*/ |
044 |
abstract class Decorator extends Component{ |
045 |
/** |
046 |
*
持有被裝飾的組建對象 |
047 |
*/ |
048 |
protected $c ; |
049 |
/** |
050 |
*
通過構造方法傳入被裝飾的對象 |
051 |
*
@param c 被裝飾的對象 |
052 |
*/ |
053 |
public function __construct( $c ){ |
054 |
$this ->c
= $c ; |
055 |
} |
056 |
|
057 |
public function calcPrize( $user , $begin , $end ){ |
058 |
//轉調組件對象的方法 |
059 |
return $this ->c->calcPrize( $user , $begin , $end ); |
060 |
} |
061 |
} |
062 |
063 |
/** |
064 |
*
裝飾器對象,計算當月業務獎金 |
065 |
*/ |
066 |
class MonthPrizeDecorator extends Decorator{ |
067 |
public function __construct( $c ){ |
068 |
parent::__construct( $c ); |
069 |
} |
070 |
|
071 |
public function calcPrize( $user , $begin , $end ){ |
072 |
//1.先獲取前面運算出來的獎金 |
073 |
$money =
parent::calcPrize( $user , $begin , $end ); |
074 |
//2.然後計算當月業務獎金,按人員和時間去獲取當月業務額,然後再乘以3% |
075 |
$prize =
TempDB::getMonthSaleMoney( $user )
* 0.03; |
076 |
echo $user . "當月業務獎金:" . $prize . "<br>" ; |
077 |
return $money + $prize ; |
078 |
} |
079 |
} |
080 |
081 |
/** |
082 |
*
裝飾器對象,計算累計獎金 |
083 |
*/ |
084 |
class SumPrizeDecorator extends Decorator{ |
085 |
public function __construct( $c ){ |
086 |
parent::__construct( $c ); |
087 |
} |
088 |
|
089 |
public function calcPrize( $user , $begin , $end ){ |
090 |
//1.先獲取前面運算出來的獎金 |
091 |
$money =
parent::calcPrize( $user , $begin , $end ); |
092 |
//2.然後計算累計獎金,其實應該按照人員去獲取累計的業務額,然後再乘以0.1% |
093 |
//簡單演示一下,假定大家累計的業務額都是1000000元 |
094 |
$prize =
1000000 * 0.001; |
095 |
echo $user . "累計業務獎金:" . $prize . "<br>" ; |
096 |
return $money + $prize ; |
097 |
} |
098 |
} |
099 |
//先創建計算基本獎金的類,這也是被修飾的類 |
100 |
$c1 = new ConcreteComponent(); |
101 |
102 |
//然後對計算的基本獎金進行裝飾,這裏要組合各個裝飾 |
103 |
//說明,各個裝飾者之間最好是不要有先後順序的限制 |
104 |
//也就是先裝飾誰和後裝飾誰都應該是一樣的 |
105 |
106 |
//組合普通業務人員的獎金計算 |
107 |
$d1 = new MonthPrizeDecorator( $c1 ); |
108 |
$d2 = new SumPrizeDecorator( $d1 ); |
109 |
110 |
//注意:這裏只需要使用最後組合好的對象調用業務方法即可,會依次調用回去 |
111 |
//日期對象都沒有用上,所以傳NULL就可以了 |
112 |
$zs = $d2 ->calcPrize( 'zs' ,
null, null); |
113 |
echo '============張三應得的獎金:' . $zs . "<br>" ; |
114 |
$ls = $d2 ->calcPrize( 'ls' ,
null, null); |
115 |
echo '============李四應得的獎金:' . $ls . "<br>" ; |
116 |
?> |