php设计模式之装饰器模式
装饰器模式又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
UML图:
角色:
组件对象的接口:可以给这些对象动态的添加职责
所有装饰器的父类:需要定义一个与组件接口一致的接口,并持有一个Component对象,该对象其实就是被装饰的对象。
具体的装饰器类:实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。
现实项目中我们怎么去采用这种设计模式呢?来看一下我的一个例子:
组件对象接口 : IBasic.php
1 <?php 2 /** 3 * 组件对象接口 4 * Interface IBasic 5 */ 6 interface IBasic 7 { 8 public function add(); 9 public function sub(); 10 }
这是一个简单的计算接口,实现加法以及减法计算。那么现实中,我有一个实现了这个接口的类,Calculation.php。已经部署,并且运行的很好。
1 <?php 2 include_once('IBasic.php'); 3 /** 4 * 待装饰对象 5 * Class Calculation 6 */ 7 class Calculation implements IBasic 8 { 9 private $num1; 10 private $num2; 11 12 public function __construct( $num1, $num2 ) 13 { 14 $this->num1 = $num1; 15 $this->num2 = $num2; 16 } 17 18 public function add() 19 { 20 return $this->num1 + $this->num2; 21 } 22 23 public function sub() 24 { 25 return $this->num1 - $this->num2; 26 } 27 }
现在突然项目的需求有变,我需要一个新的计算方法,比如我传入两个数 5 和 2。我想得到这样的结果 (5 + 2) + ( 5 -2 ),可以看到新的需求正好是我的原有计算类的加法运算和减法运算的整合。但是现在我的类在线上项目中运行良好,此时我去修改这个类
不现实,而且成本也很大。这时可能会想到,我给Calculation 类派生出一个子类,这样再去计算不就好了吗?如果我这样:
1 <?php 2 3 class ChildClass extends Calculation 4 { 5 6 }
其实我只要简单的组合一下现有的计算方法就行了,我用派生子类ChildClass却继承了大量的不需要的方法。而且ChildClass这个子类和Calculation的耦合度非常的大。那我们用 装饰器模式 怎么去解决这个问题呢?
我们新建一个所有装饰器的父类:DecoratorParent.php
1 <?php 2 include_once('IBasic.php'); 3 /** 4 * 所有装饰器父类 5 * Class Clothes 6 */ 7 class DecoratorParent implements IBasic 8 { 9 protected $component; 10 11 function Decorate(IBasic $component) 12 { 13 $this->component = $component; 14 } 15 16 //实现加法 17 public function add() 18 { 19 if( !empty( $this->component ) ){ 20 return $this->component->add(); 21 } 22 } 23 24 //实现减法 25 public function sub() 26 { 27 if( !empty( $this->component ) ){ 28 return $this->component->sub(); 29 } 30 } 31 }
具体的装饰器类:MixCalculation.php
1 <?php 2 include_once('DecoratorParent.php'); 3 class MixCalculation extends DecoratorParent 4 { 5 //混合运算 6 public function mixCalculation() 7 { 8 return parent::add() + parent::sub(); 9 } 10 }
我们新建一个具体的混合计算装饰器类。这样这个混合计算类就能满足我的新需求了。来看一下Client.php怎么去访问吧:
1 <?php 2 class Client 3 { 4 public function __construct() 5 { 6 $obj = new Calculation( 5, 2 ); 7 echo $obj->add(); 8 echo "<hr/>"; 9 10 $objR = new MixCalculation(); 11 $objR->Decorate( $obj ); 12 echo $objR->mixCalculation(); 13 } 14 } 15 16 new Client();
运行结果:
7
————————————————————
10
这样如果我有一个 (5 + 2) - ( 5 -2 )的计算需求的时候,我只需要派生出具体的装饰器类就可以轻松实现了。而且对原来的Calculation不产生任何的影响。
适用场景:
1. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
2. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
3. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
参考文章:
https://my.oschina.net/u/2440672/blog/600338
http://blog.csdn.net/jhq0113/article/details/45458133