设计模式(四)原型模式Prototype(创建型)

1.   概述

我们都知道,创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象。

例子1:孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来。

例子2:寄个快递
下面是一个邮寄快递的场景:
“给我寄个快递。”顾客说。
“寄往什么地方?寄给……?”你问。
“和上次差不多一样,只是邮寄给另外一个地址,这里是邮寄地址……”顾客一边说一边把写有邮寄地址的纸条给你。
“好!”你愉快地答应,因为你保存了用户的以前邮寄信息,只要复制这些数据,然后通过简单的修改就可以快速地创建新的快递数据了。

2. 问题

当对象的构造函数非常复杂,在生成新对象的时候非常耗时间、耗资源的情况?我们是怎么来创建呢?

3. 解决方案

       通过复制(克隆、拷贝)一个指定类型的对象来创建更多同类型的对象。这个指定的对象可被称为“原型”对象,也就是通过复制原型对象来得到更多同类型的对象。即原型设计模式。在php的很多模板库,都用到clone。如smarty等。

4. 适用性

原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是有对象的内部提供克隆的方法,通过该方法返回一个对象的副本,这种创建对象的方式,相比我们之前说的几类创建型模式还是有区别的,之前的讲述的工厂模式与抽象工厂都是通过工厂封装具体的new操作的过程,返回一个新的对象,有的时候我们通过这样的创建工厂创建对象不值得,特别是以下的几个场景的时候,可能使用原型模式更简单也效率更高。

• 1)当一个系统应该独立于它的产品创建、构成和表示时,要使用 Prototype模式

• 2)当要实例化的类是在运行时刻指定时,例如,通过动态装载;

• 3)为了避免创建一个与产品类层次平行的工厂类层次时

• 4)当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。(也就是当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适)。

5. 结构

     原型模式结构如下页上图所示:

       1.gif

6. 组成

客户(Client)角色:使用原型对象的客户程序
抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)

具体原型(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象。此角色需要实现抽象原型角色所要求的接口。

7. 效果

Prototype模式有许多和Abstract Factory模式 和 Builder模式一样的效果:它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。此外,这些模式使客户无需改变即可使用与特定应用相关的类。
下面列出Prototype模式的另外一些优点。
1 ) 运行时刻增加和删除产品: Prototype允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。它比其他创建型模式更为灵活,因为客户可以在运行时刻建立和删除原型。
2 ) 改变值以指定新对象: 高度动态的系统允许你通过对象复合定义新的行为—例如,通过为一个对象变量指定值—并且不定义新的类。你通过实例化已有类并且将这些实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。这种设计使得用户无需编程即可定义新“类” 。实际上,克隆一个原型类似于实例化一个类。Prototype模式可以极大的减少系统所需要的类的数目。
3) 改变结构以指定新对象:许多应用由部件和子部件来创建对象。
4) 减少子类的构造 Factory Method 经常产生一个与产品类层次平行的 Creator类层次。Prototype模式使得你克隆一个原型而不是请求一个工厂方法去产生一个新的对象。因此你根本不需要Creator类层次。这一优点主要适用于像 C + +这样不将类作为一级类对象的语言。像Smalltalk和Objective C这样的语言从中获益较少,因为你总是可以用一个类对象作为生成者。在这些语言中,类对象已经起到原型一样的作用了。
5) 用类动态配置应用 一些运行时刻环境允许你动态将类装载到应用中。在像 C + +这样的语言中,Prototype模式是利用这种功能的关键。一个希望创建动态载入类的实例的应用不能静态引用类的构造器。而应该由运行环境在载入时自动创建每个类的实例,并用原型管理器来注册这个实例(参见实现一节) 。这样应用就可以向原型管理器请求新装载的类的实例,这些类原本并没有和程序相连接。 E T + +应用框架[ W G M 8 8 ]有一个运行系统就是使用这一方案的。

Prototype的主要缺陷是每一个Prototype的子类都必须实现clone操作,这可能很困难。

如,当所考虑的类已经存在时就难以新增 clone操作。当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。

8. 实现

<?php  
/** 
 * 原型模式  
 */  
   
/** 
 * 抽象原型角色 
 */  
interface Prototype {  
    public function copy();  
}  
   
/** 
 * 具体原型角色 
 */  
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 copy() {  
       /** 深拷贝 */  
       return  clone  $this;      
       /** 浅拷贝 */  
       //return  $this;     
    }  
}  
  
   
class Client {  
   
     /** 
     * Main program. 
     */  
    public static function main() {  
        $object1 = new ConcretePrototype(11);  
        $object_copy = $object1->copy();  
  
        var_dump($object1->getName());  
        echo '<br />';  
        var_dump($object_copy->getName());  
        echo '<br />';  
   
        $object1->setName(22);  
        var_dump($object1->getName());  
        echo '<br />';  
        var_dump($object_copy->getName());  
        echo '<br />';  
    }  
}  
   
Client::main();  
?>

9. 浅拷贝和深拷贝

原型模式的原理图:

3.png

浅拷贝

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

浅复制后的对象和对象副本的情况:

设计模式(四)原型模式Prototype(创建型)

深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。

深复制的对象和对象副本的情况:

4.jpg

深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。

10. 带Prototype Manager的原型模式

     原型模式的第二种形式是带原型管理器的原型模式,其UML图如下:

     5.jpg

       原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。

       下面这个例子演示了在原型管理器中存储用户预先定义的颜色原型,客户通过原型管理器克隆颜色对象。

<?php  
/** 
 * abstract Prototype 
 * 
 */  
abstract class ColorPrototype  
{  
  //Methods  
    abstract function  copy();  
}  
  
/** 
 * Concrete Prototype 
 * 
 */  
class Color extends ColorPrototype{  
    //Fields  
    private  $red;  
    private  $green;  
    private  $blue;  
    //Constructors  
    function __construct( $red, $green, $red) {  
        $this->red = $red;  
        $this->green = $green;  
        $this->blue = $red;  
     }  
     /** 
      * set red 
      * 
      * @param unknown_type $red 
      */  
    public  function setRed($red) {  
        $this->red = $red;  
     }  
       
     /** 
      * get red 
      * 
      */  
    public  function getRed(){  
        return  $this->red;  
     }  
     /** 
      *set Green 
      * 
      * @param  $green 
      */  
    public  function setGreen($green) {  
        $this->green = $green;  
     }  
     /** 
      * get Green 
      * 
      * @return unknown 
      */  
    public  function getGreen() {  
        return  $this->green ;  
     }  
     /** 
      *set Blue 
      * 
      * @param  $Blue 
      */  
    public  function setBlue($Blue) {  
        $this->blue = $Blue;  
    }  
     /** 
      * get Blue 
      * 
      * @return unknown 
      */  
    public  function getBlue() {  
        return  $this->blue ;  
    }  
      
    /** 
     * Enter description here... 
     * 
     * @return unknown 
     */  
    function copy(){  
        return clone $this;  
    }  
      
    function display() {  
        echo $this->red , ',', $this->green, ',', $this->blue ,'<br>';  
    }  
}  
/** 
 * Enter description here... 
 * 
 */  
class ColorManager  
{  
    // Fields  
    static  $colors = array();  
    // Indexers  
    public static function add($name, $value){  
        self::$colors[$name] = $value;  
    }  
      
    public static function getCopy($name) {  
        return   self::$colors[$name]->copy();  
    }  
}  
/** 
 *Client 
 * 
 */  
class Client  
{  
    public static function  Main()  
    {  
        //原型:白色  
        ColorManager::add("white", new Color( 255, 0, 0 ));  
          
        //红色可以由原型白色对象得到,只是重新修改白色: r  
        $red = ColorManager::getCopy('white');  
        $red->setRed(255);  
        $red->display();  
          
        //绿色可以由原型白色对象得到,只是重新修改白色: g  
        $green = ColorManager::getCopy('white');  
        $green->setGreen(255);  
        $green->display();  
          
        //绿色可以由原型白色对象得到,只是重新修改白色: b  
        $Blue = ColorManager::getCopy('white');  
        $Blue->setBlue(255);  
        $Blue->display();  
    }  
}  
  
ini_set('display_errors', 'On');  
error_reporting(E_ALL & ~ E_DEPRECATED);  
Client::Main();  
?>

转自:http://blog.csdn.net/hguisu/article/details/7518947

原创文章,作者:s19930811,如若转载,请注明出处:http://www.178linux.com/2945

(0)
s19930811s19930811
上一篇 2015-06-25 10:12
下一篇 2015-06-26 10:10

相关推荐

  • 常用的命令

    1、Cat 查看文件内容 2、Cp 拷贝    -a:此参数的效果和同时指定“-dpR”参数相同;    -d:当复制符号连接时,把目标文件或目录也建立为符号连接,并指向与源文件或目录连接的原始文件或目录;    -f:强行复制文件或目录,不论目…

    Linux干货 2017-04-09
  • 马哥教育网络班20期+第一周课程练习

    一、计算机的组成及其功能。 计算机是由几个单元所组成,输入单元,输出单元,运算器,控制器,存储器,5大单元组成  1、运算器 又称运算器又称算术逻辑单元,它是计算机对数据进行加工处理的部件,包括算术运算(加、减、乘、除等)和逻辑运算(与、或、非、异或、比较等)。 2、控制器 负责从存储器中取出指令,并对指令进行译码;根据指令的要求,按时间的先后顺序…

    Linux干货 2016-06-23
  • VIM 文 本 编 辑 工 具-20160808

    VIM 文 本 编 辑 工 具 §·学习大纲   一·使用vi和vim的三种主要模式 二·扩展模式下基本操作 三·命令模式下基本操作 四·编辑模式下基本操作 五·VIM寄存器 六·编辑二进制文件 七·可视化模式 八·多文件模式 九·使用多个“窗口” 十·定制vim的工作特性 十一·VIM了解更多 十二·练习题     §·使用v…

    Linux干货 2016-08-10
  • 铭记今天

        2016年10月19日,我做了一个很艰难的决定,选择了马哥24期网络全程班,就算是冲动也好,无论对错都要坚持下去。 从出来工作之后,一直做销售这块,虽然跟电脑有关,但是其中的艰苦只有自己明白。慢慢的年级上来了,手中没有什么技术,做人生活,总觉得少了什么,总觉得自己以后会被社会淘汰,心一天一天着急。在后选择了一个从来都没有涉及的运维…

    Linux干货 2016-10-19
  • 高级文件系统管理1

    本部分内容主要讲述了高级文件系统的管理,包括设定文件系统配额,设定和管理软RAID设备,配置逻辑卷,设定LVM快照和btrfs文件系统。其中文件系统配额和软RAID在企业中使用的频率并不很高,作为熟练即可,而逻辑卷的创建和LVM快照是非常重要的内容,必须精通并完全理解其原理。至于btrfs文件系统是新兴的一种技术,作为了解即可。 一、知识整理 1、文件系统配…

    Linux干货 2016-08-29
  • 逻辑卷管理—LVM

    逻辑卷管理—LVM   LVM:Logical  Volume  Manager        使用软件方式来组织一个或多个底层硬件设备为一个抽象的逻辑设备。 1、查看分区情况: 2、创建分区:(更改分区类型为8e:Linux LVM) 3、创建分区成功: 5、创建物理…

    Linux干货 2016-09-01