原型模式(Prototype)
意图:
【GoF】用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
动机:
虽然抽象工厂把创建者与产品实现分离,但是也造成了每次添加产品家族时,都要创建一个相关的具体创建者,在一个快速增长的系统中,随着包含的产品越来越多,维护这种关系将会变得越来越复杂。为了避免这种情况,我们可以利用PHP中的clone关键字,生成产品对象自身的克隆,具体的产品类本身便成为了它们生成自己的基础。这样做不仅可以促进代码的灵活性,还能够减少创建的对象数目。使用组合代替了继承。
适用:
一、当要实例化的类是在运行时刻指定时。
二、为了避免创建一个与产品类层次平行的工厂类层次时。
三、当一个类的实例只能有几个不同状态组合中的一种时。
Prototype模式优点:
1、可以在运行时刻增加和删除产品
2、可以改变值或结构以指定新对象
3、减少子类的构造
4、用类动态配置应用
Prototype模式的缺点:
Prototype是的最主要的缺点就是每一个类必须包含一个克隆方法;
而且这个克隆方法需要对类的功能进行检测,这对于全新的类来说较容易,但对已有的类进行改造时将不是件容易的事情;
结构图:
Prototype:声明一个克隆自身的接口。
ConcretePrototype:实现一个克隆自身的操作。
Client:让一个原型克隆自身从而创建一个新的对象。
执行方式:
客户请求原型克隆自身。
示例代码一:
class Sea{ protected $navigability=0; function __construct($navigability){ $this->navigability = $navigability; } } class EarthSea extends Sea{} class MarsSea extends Sea{} class Plains{ protected $navigability=0; function __construct($navigability){ $this->navigability = $navigability; } } class EarthPlains extends Plains{} class MarsPlains extends Plains{} class Forest{ protected $navigability=0; function __construct($navigability){ $this->navigability = $navigability; } } class EarthForest extends Forest{ function getnavigability(){ return $this->navigability; } } class MarsForest extends Forest{} class TerrainFactory{ private $sea; private $forest; private $Palins; function __construct(Sea $sea,Plains $plains,Forest $forest){ $this->sea = $sea; $this->plains = $plains; $this->forest = $forest; } function getSea(){ return clone $this->sea; } function getPlains(){ return clone $this->plains; } function getForest(){ return clone $this->forest; } } $factory = new TerrainFactory(new EarthSea(-4),new EarthPlains(2),new EarthForest(3)); print_r($factory->getSea()); print_r($factory->getPlains()); print_r($factory->getForest()->getnavigability());
这个例子的结构图:
这里的clone方法是浅复制,即如果被克隆的对象引用了其他的对象,那么克隆出来的对象也是引用了以前的对象,这样当被克隆的对象改变了引用的对象时,克隆出来的对象所引用的对象也会被改变了。这种情况就是值对象模式出现的问题原理是相同的。我们可以使用拦截器__clone()方法实现深复制。
如果是浅复制的情况下改变值:
class Contained { public $now = 5; } class Container { public $contained; function __construct() { $this->contained = new Contained(); } // function __clone() { // $this->contained = clone $this->contained; // } } $original = new Container(); $copy = clone $original; $original->contained->now = -1;//改变引用的值对象 print_r( $original ); print_r( $copy );
结果:
Result:Container Object ( [contained] => Contained Object ( [now] => -1 ) ) Container Object ( [contained] => Contained Object ( [now] => -1 ) )
class Contained { //被引用的对象 public $now = 5; } class Container { public $contained; function __construct() { $this->contained = new Contained(); } function __clone() { $this->contained = clone $this->contained; } } $original = new Container(); $copy = clone $original; $original->contained->now = -1; print_r( $original ); print_r( $copy );
结果:
Result:Container Object ( [contained] => Contained Object ( [now] => -1 ) ) Container Object ( [contained] => Contained Object ( [now] => 5 ) )
序列化与反序列化实现深拷贝:
class Contained { public $now = 5; } class Container { public $contained; function __construct() { $this->contained = new Contained(); } function copy(){ $serialize_obj = serialize($this); // 序列化 $clone_obj = unserialize($serialize_obj); // 反序列化 return $clone_obj; } } $original = new Container(); $copy = $original ->copy(); $original->contained->now = -1; print_r( $original ); print_r( $copy ); var_dump($original); var_dump($copy);
结果:
Result: Container Object ( [contained] => Contained Object ( [now] => -1 ) ) Container Object ( [contained] => Contained Object ( [now] => 5 ) ) object(Container)#1 (1) { ["contained"]=> object(Contained)#2 (1) { ["now"]=> int(-1) } } object(Container)#3 (1) { ["contained"]=> object(Contained)#4 (1) { ["now"]=> int(5) } }
在序列化的过程中对象被重新创建。在序列化与反序列化的过程中可以执行__sleep()与__wakeup()函数:
class myClass{ public $myContent; function __construct($string){ $this->myContent = $string; } public function __sleep(){ $this->myContent = '这是我的秘密'; return array('myContent'); } public function __wakeup(){ $this->myContent = '我爱宋祖英'; //反序列化就不用返回数组了,就是对应的字符串的解密,字符串已经有了就不用其他的了 } } $content = new myClass('我爱宋祖英,这是一个秘密');
print_r(serialize($content)); print_r(unserialize(serialize($content)));
结果:
Result:O:7:"myClass":1:{s:9:"myContent";s:18:"这是我的秘密";} myClass Object ( [myContent] => 我爱宋祖英 )
示例代码二:
//用原型实例指定创建对象的种类.并且通过拷贝这个原型来创建新的对象 //声明一个克隆自身的接口,即抽象原型角色 interface Prototype{ public function copy(); } //实现克隆自身的操作,具体原型角色 class ConcretePrototype implements Prototype{ private $name; function __construct($name){ $this->name = $name; } function getName(){ return $this->name; } function setName($name){ $this->name = $name; } //克隆 function copy(){ //浅拷贝 //return clone $this; //深拷贝 $serialize_obj = serialize($this); //序列化 $clone_obj = unserialize($serialize_obj); //反序列化 return $clone_obj; } } //测试深拷贝的类 class Test{ public $array; } class Client{ //实现原型模式 public static function main(){ $test = new Test(); $test->array = array('1','2','3'); $pro1 = new ConcretePrototype($test); print_r($pro1->getName()); $test->array = array('2','3','4'); $pro2 = $pro1->copy(); echo '<br />'; print_r($pro2->getName()); } } Client::main();
结果:
Test Object ( [array] => Array ( [0] => 1 [1] => 2 [2] => 3 ) ) Test Object ( [array] => Array ( [0] => 2 [1] => 3 [2] => 4 ) )