设计模式(四)原型模式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

相关推荐

  • 如何安装VMware Workstation

    1、打开安装包,直接点击“下一步” 2、选择接受条款,单击“下一步” 3、选择安装路径,单击“下一步” 4、单击“下一步”,也可以勾选掉两个选项   5、单击“下一步”   6、点击“安装”   7、完成安装  

    2017-07-11
  • 入门——计算机基础简介

    一、计算机系统 计算机系统:由硬件(Hardware)系统和软件(Software)系统俩大部分组成 二、计算机硬件 计算机(computer):是一种能接收和存储信息,并按照存储在其内部的程序对海量数据进行自动、高速的处理,然后把处理结果输出的现代化电子设备。 计算机硬件组成部分 冯.诺依曼体系结构: 1946年数学家冯.诺依曼提出运算器、控制器、存储器、…

    2018-03-27
  • 第一周博客作业

    计算机的组成 CPU:运算器、控制器、寄存器、缓存 存储器:内存 RAM(Random-Access Memory) Input:输入指令、提供数据 Output: 输出数据加工后的结果 功能: 输入设备负责把用户的信息输入到计算机中 输出设备负责将计算机中的信息(包括程序和数据)传送到外部媒介,供用户查看或保存; 存储器负责存储数据和程序,并根据控制命令提…

    Linux干货 2017-01-10
  • Ansible 详细用法说明

    Ansible 一、概述 运维工具按需不需要有代理程序来划分的话分两类: agent(需要有代理工具):基于专用的agent程序完成管理功能,puppet, func, zabbix agentless(无须代理工具):基于ssh服务完成管理,ansible, fabric 二、简介   Ansible是一个简单的自动化运维管理工具,基于Python语言实现…

    2016-11-07
  • Linux开学一周的学习心得

    Linux,这个名字第一次听到是通过张超老师给我讲解,因为我在学校学的是金融数学,平时对这方面没有太多接触。当时对Linux就知道是个系统,随后在网上搜了搜,了解也不太多。 直到今天已经来马哥教育一星期了,刚入学的这一星期上课,都是一些基本的命令,感觉学着还蛮有意思,教学的安排也非常合理,我们大家学一天还能自己动手操作一天,既能专心听课,又能把学到的知识巩固…

    2017-07-15
  • 分布式文件系统Mogilefs介绍与安装

    一、分布式文件系统简介: 什么是分布式存储:       分布式存储系统,是将数据分散存储在多台独立的设备上。传统的网络存储系统采用集中的存储服务器存放所有数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能满足大规模存储应用的需要。分布式网络存储系统采用可扩展的系统结构,利用多台存储…

    Linux干货 2016-05-04