zoukankan      html  css  js  c++  java
  • 【PHP设计模式】创建型之原型模式(Prototype)

    原型模式(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 ) ) 
  • 相关阅读:
    雷林鹏分享:Lua if 嵌套语句
    雷林鹏分享:Lua if...else 语句
    雷林鹏分享:Lua if 语句
    debug 使用lldb
    mysql高负载的问题排查
    一个NULL引发的血案
    画椭圆型
    异常分析
    go 语言的库文件放在哪里?如何通过nginx代理后还能正确获取远程地址
    defer 要放在return之前
  • 原文地址:https://www.cnblogs.com/colorstory/p/2717798.html
Copyright © 2011-2022 走看看