设计模式(四)原型模式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)
上一篇 2015-06-25 10:12
下一篇 2015-06-26 10:10

相关推荐

  • crond实现邮件告警

    实验环境:centos 6.8 所需软件:mailx或msmtp或smtpEmail等 smtp客户端程序 所需帐号:我这使用的是163邮箱。 本方案优点:安装软件少,无须启动postfix等邮件服务。无须配置postfix邮件服务。由于调用的是注册邮箱,所以也不会给移入到垃圾邮件中。  建议:生产中,不管是什么服务发送邮件的建议使用外部smtp帐…

    Linux干货 2016-07-10
  • 第六周作业

    vim文本编辑器基本用法     基本模式:         编辑模式,命令模式    输入模式        末行模式:    …

    Linux干货 2016-09-19
  • 内核相关

    内核两大流派单内核微内核 grub分为三个阶段:1阶段 1.5阶段 2阶段 内核阶段 通过/proc/cmdline找到根并挂载2阶段内容是放在/boot/grub目录下Centos6 实验:丢失除grub.conf外所有文件没有修复过的时候/boot/grub下文件是放在磁盘上某个位置的修复过一次后/boot/grub下文件就起作用了,一旦执行过grub-…

    Linux干货 2018-01-03
  • 关于大型网站技术演进的思考(十四)–网站静态化处理—前后端分离—上(6)

    原文出处: 夏天的森林    前文讲到了CSI技术,这就说明网站静态化技术的讲述已经推进到了浏览器端了即真正到了web前端的范畴了,而时下web前端技术的前沿之一就是前后端 分离技术了,那么在这里网站静态化技术和前后端分离技术产生了交集,所以今天我将讨论下前后端分离技术,前后端分离技术讨论完后,下一篇文章我将会以网站 静态化技术…

    Linux干货 2015-03-11
  • FHS文件系统介绍及各目录功能说明

    作者:M21-陆东贵 FHS文件系统介绍及各目录功能说明 FHS简介 Filesystem Hierarchy Standard(文件系统目录标准)的缩写,多数Linux版本采用这种文件组织形式,类似于Windows操作系统中c盘的文件目录,FHS采用树形结构组织文件。FHS定义了系统中每个区域的用途、所需要的最小构成的文件和目录,同时还给出了例外处理与矛盾…

    Linux干货 2016-10-18
  • 第三周作业

    1. 列出当前系统上所有已经登录的用户的用户名,注意:同一个用户登录多次则显示一次即可。 who | cut -d' ' -f1 | sort -u 2. 取出最后登录当前系统的用户的相关信息 grep  "$(who …

    Linux干货 2016-12-19