【PHP设计模式】创建型之原型模式(Prototype) 原型模式(Prototype)

意图:

  【GoF】用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

动机:

   虽然抽象工厂把创建者与产品实现分离,但是也造成了每次添加产品家族时,都要创建一个相关的具体创建者,在一个快速增长的系统中,随着包含的产品越来越多,维护这种关系将会变得越来越复杂。为了避免这种情况,我们可以利用PHP中的clone关键字,生成产品对象自身的克隆,具体的产品类本身便成为了它们生成自己的基础。这样做不仅可以促进代码的灵活性,还能够减少创建的对象数目。使用组合代替了继承。

适用:

  一、当要实例化的类是在运行时刻指定时。

  二、为了避免创建一个与产品类层次平行的工厂类层次时。

  三、当一个类的实例只能有几个不同状态组合中的一种时。

Prototype模式优点:

  1、可以在运行时刻增加和删除产品

  2、可以改变值或结构以指定新对象

  3、减少子类的构造

  4、用类动态配置应用

Prototype模式的缺点:

  Prototype是的最主要的缺点就是每一个类必须包含一个克隆方法;

  而且这个克隆方法需要对类的功能进行检测,这对于全新的类来说较容易,但对已有的类进行改造时将不是件容易的事情;

结构图:

【PHP设计模式】创建型之原型模式(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());

  这个例子的结构图:

【PHP设计模式】创建型之原型模式(Prototype)
原型模式(Prototype)

  这里的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 ) )