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