设计模式[创建型]07--原型模式(Prototype)

一、简介

相比正常创建一个对象 (new Foo () ),首先创建一个原型,然后克隆它会更节省开销。

原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。适用于大对象的创建,因为创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。

二、场景

比如:

  • 大量的数据对象(比如通过ORM获取1,000,000行数据库记录然后创建每一条记录对应的对象实体)

三、类结构

原型模式主要角色如下:

角色 类别 简述
Prototype 抽象原型角色 声明一个克隆自身的接口
Concrete Prototype 具体原型角色 实现一个克隆自身的操作

四、UML图

五、代码分析

1、抽象原型角色

interface Prototype
{
    public function shallowCopy();
    public function deepCopy();
}

2、具体原型角色

class ConcretePrototype implements Prototype
{
    private $_name;

    public function __construct($name){
        $this->_name = $name;
    }
    
    public function setName($name){
        $this->_name = $name;
    }

    public function getName(){
        return $this->_name;
    }

    //浅拷贝
    public function shallowCopy(){
        return clone $this; 
    }

    //深拷贝
    public function deepCopy() {
        $serialize_obj = serialize($this);
        $clone_obj = unserialize($serialize_obj);
        return $clone_obj;
    }
}

3、使用案例

class Demo
{
    public $string;
}

class UsePrototype
{

    //测试浅拷贝
    public function shallow(){
        $demo = new Demo();
        $demo->string = "susan";
        $object_shallow_first = new ConcretePrototype($demo);
        $object_shallow_second = $object_shallow_first->shallowCopy();
        
        var_dump($object_shallow_first->getName());
        echo '<br/>';
        var_dump($object_shallow_second->getName());
        echo '<br/>';
        
        $demo->string = "sacha";
        var_dump($object_shallow_first->getName());
        echo '<br/>';
        var_dump($object_shallow_second->getName());
        echo '<br/>';
        
    }
    
    //测试深拷贝
    public function deep(){
        $demo = new Demo();
        $demo->string = "Siri";
        $object_deep_first = new ConcretePrototype($demo);
        $object_deep_second = $object_deep_first->deepCopy();
    
        var_dump($object_deep_first->getName());
        echo '<br/>';
        var_dump($object_deep_second->getName());
        echo '<br/>';
    
        $demo->string = "Demo";
        var_dump($object_deep_first->getName());
        echo '<br/>';
        var_dump($object_deep_second->getName());
        echo '<br/>';
    }
  
}

$up = new \Libs\UsePrototype;
$up->shallow();
echo '<hr>';
$up->deep();

结果如下:

object(Demo)#2 (1) { ["string"]=> string(5) "susan" } 
object(Demo)#2 (1) { ["string"]=> string(5) "susan" } 
object(Demo)#2 (1) { ["string"]=> string(5) "sacha" } 
object(Demo)#2 (1) { ["string"]=> string(5) "sacha" } 
--------------------------------------------------------------
object(Demo)#5 (1) { ["string"]=> string(4) "Siri" } 
object(Demo)#8 (1) { ["string"]=> string(4) "Siri" } 
object(Demo)#5 (1) { ["string"]=> string(4) "Demo" } 
object(Demo)#8 (1) { ["string"]=> string(4) "Siri" } 

注意:在PHP中,对象本身就是引用传递。

六、特点

1、浅拷贝

被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。

2、深拷贝

被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。

即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。

3、序列化深拷贝

利用序列化来做深拷贝,把对象写到流里的过程是序列化的过程,这一过程称为“冷冻”或“腌咸菜”,反序列化对象的过程叫做“解冻”或“回鲜”。

在PHP中使用serialize和unserialize函数实现序列化和反序列化。

推荐阅读更多精彩内容