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

1.   概述

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

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

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

2. 问题

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

3. 解决方案

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

4. 适用性

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

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

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

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

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

5. 结构

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

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. 实现
  1. <?php
  2. /**
  3. * 原型模式
  4. */
  5. /**
  6. * 抽象原型角色
  7. */
  8. interface Prototype {
  9. public function copy();
  10. }
  11. /**
  12. * 具体原型角色
  13. */
  14. class ConcretePrototype implements Prototype{
  15. private  $_name;
  16. public function __construct($name) {
  17. $this->_name = $name;
  18. }
  19. public function setName($name) {
  20. $this->_name = $name;
  21. }
  22. public function getName() {
  23. return $this->_name;
  24. }
  25. public function copy() {
  26. /** 深拷贝 */
  27. return  clone  $this;
  28. /** 浅拷贝 */
  29. //return  $this;
  30. }
  31. }
  32. class Client {
  33. /**
  34. * Main program.
  35. */
  36. public static function main() {
  37. $object1 = new ConcretePrototype(11);
  38. $object_copy = $object1->copy();
  39. var_dump($object1->getName());
  40. echo '<br />';
  41. var_dump($object_copy->getName());
  42. echo '<br />';
  43. $object1->setName(22);
  44. var_dump($object1->getName());
  45. echo '<br />';
  46. var_dump($object_copy->getName());
  47. echo '<br />';
  48. }
  49. }
  50. Client::main();
  51. ?>
9. 浅拷贝和深拷贝
原型模式的原理图:
浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
浅复制后的对象和对象副本的情况:

深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。
深复制的对象和对象副本的情况:
深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。
 

10. 带Prototype Manager的原型模式

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

     

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

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

  1. <?php
  2. /**
  3. * abstract Prototype
  4. *
  5. */
  6. abstract class ColorPrototype
  7. {
  8. //Methods
  9. abstract function  copy();
  10. }
  11. /**
  12. * Concrete Prototype
  13. *
  14. */
  15. class Color extends ColorPrototype{
  16. //Fields
  17. private  $red;
  18. private  $green;
  19. private  $blue;
  20. //Constructors
  21. function __construct( $red, $green, $red) {
  22. $this->red = $red;
  23. $this->green = $green;
  24. $this->blue = $red;
  25. }
  26. /**
  27. * set red
  28. *
  29. * @param unknown_type $red
  30. */
  31. public  function setRed($red) {
  32. $this->red = $red;
  33. }
  34. /**
  35. * get red
  36. *
  37. */
  38. public  function getRed(){
  39. return  $this->red;
  40. }
  41. /**
  42. *set Green
  43. *
  44. * @param  $green
  45. */
  46. public  function setGreen($green) {
  47. $this->green = $green;
  48. }
  49. /**
  50. * get Green
  51. *
  52. * @return unknown
  53. */
  54. public  function getGreen() {
  55. return  $this->green ;
  56. }
  57. /**
  58. *set Blue
  59. *
  60. * @param  $Blue
  61. */
  62. public  function setBlue($Blue) {
  63. $this->blue = $Blue;
  64. }
  65. /**
  66. * get Blue
  67. *
  68. * @return unknown
  69. */
  70. public  function getBlue() {
  71. return  $this->blue ;
  72. }
  73. /**
  74. * Enter description here...
  75. *
  76. * @return unknown
  77. */
  78. function copy(){
  79. return clone $this;
  80. }
  81. function display() {
  82. echo $this->red , ',', $this->green, ',', $this->blue ,'<br>';
  83. }
  84. }
  85. /**
  86. * Enter description here...
  87. *
  88. */
  89. class ColorManager
  90. {
  91. // Fields
  92. static  $colors = array();
  93. // Indexers
  94. public static function add($name, $value){
  95. self::$colors[$name] = $value;
  96. }
  97. public static function getCopy($name) {
  98. return   self::$colors[$name]->copy();
  99. }
  100. }
  101. /**
  102. *Client
  103. *
  104. */
  105. class Client
  106. {
  107. public static function  Main()
  108. {
  109. //原型:白色
  110. ColorManager::add("white", new Color( 255, 0, 0 ));
  111. //红色可以由原型白色对象得到,只是重新修改白色: r
  112. $red = ColorManager::getCopy('white');
  113. $red->setRed(255);
  114. $red->display();
  115. //绿色可以由原型白色对象得到,只是重新修改白色: g
  116. $green = ColorManager::getCopy('white');
  117. $green->setGreen(255);
  118. $green->display();
  119. //绿色可以由原型白色对象得到,只是重新修改白色: b
  120. $Blue = ColorManager::getCopy('white');
  121. $Blue->setBlue(255);
  122. $Blue->display();
  123. }
  124. }
  125. ini_set('display_errors', 'On');
  126. error_reporting(E_ALL & ~ E_DEPRECATED);
  127. Client::Main();
  128. ?>

设计模式(四)原型模式Prototype(创建型)的更多相关文章

  1. 原型模式 prototype 创建型 设计模式(七)

    原型模式  prototype 意图 用原型实例指定需要创建的对象的类型,然后使用复制这个原型对象的方法创建出更多同类型的对象   显然,原型模式就是给出一个对象,然后克隆一个或者更多个对象 小时候看 ...

  2. Java设计模式05:常用设计模式之原型模式(创建型模式)

    1. Java之原型模式(Prototype Pattern)     原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. ...

  3. 设计模式05: Prototype 原型模式(创建型模式)

    Prototype 原型模式(创建型模式) 依赖关系的倒置抽象不应该依赖于实现细节,细节应该依赖于抽象.对所有的设计模式都是这样的. -抽象A直接依赖于实现细节b -抽象A依赖于抽象B,实现细节b依赖 ...

  4. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...

  5. 二十四种设计模式:原型模式(Prototype Pattern)

    原型模式(Prototype Pattern) 介绍用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象.示例有一个Message实体类,现在要克隆它. MessageModel usin ...

  6. 创建型设计模式之原型模式(Prototype)

    结构   意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或 ...

  7. 设计模式 笔记 原型模式 prototype

    //---------------------------15/04/07---------------------------- //prototype 原型模式--对象创建型模式 /* 1:意图: ...

  8. php设计模式四 ---- 原型模式

    1.简介 用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 主要解决:在运 ...

  9. 【UE4 设计模式】原型模式 Prototype Pattern

    概述 描述 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.如孙悟空猴毛分身.鸣人影之分身.剑光分化.无限剑制 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, ...

  10. [设计模式] 4 原型模式 prototype

    设计模式:可复用面向对象软件的基础>(DP)本文介绍原型模式和模板方法模式的实现.首先介绍原型模式,然后引出模板方法模式. DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创 ...

随机推荐

  1. BZOJ 1022 小约翰的游戏 (Anti-Nim游戏)

    题解:注意题目中规定取到最后一粒石子的人算输,所以是Anti-Nim游戏,胜负判断为: 先手必胜: 1.所有堆的石子数都为1且游戏的SG值为0: 2.有些堆的石子数大于1且游戏的SG值不为0. #in ...

  2. Flex 事件机制

    使用ActionScript的单击事件示例 <?xml version="1.0" encoding="utf-8"?> <s:Applica ...

  3. 《数字图像处理原理与实践(MATLAB版)》一书之代码Part6

    本文系<数字图像处理原理与实践(MATLAB版)>一书之代码系列的Part6,辑录该书第281至第374页之代码,供有须要读者下载研究使用.代码运行结果请參见原书配图,建议下载代码前阅读下 ...

  4. 《Java虚拟机原理图解》 1.2.2、Class文件里的常量池具体解释(上)

    [last updated:2014/11/27] NO1.常量池在class文件的什么位置? 我的上一篇文章<Java虚拟机原理图解> 1.class文件基本组织结构中已经提到了clas ...

  5. 使用LiveWriter发布Orchard博客

    我们可以Windows Live Writer来发布Orchard博客 在Dashboard–> Modules菜单找到 Remote Blog Publishing 模块.点击 Enable ...

  6. js中的setTimeout和setInterval

    在html页面中要使用自动刷新功能时,可以是使用js中setTimeout和setInterval: 一.使用方法 setTimeout的使用setTimeout('要调用的Js方法', 调用的延迟时 ...

  7. Opencv 完美配置攻略 2014 (Win8.1 + Opencv 2.4.8 + VS 2013)下

    前面说了一些在OpenCV在VS2013下的配置的过程,下面说一下其中的个别的知识点,了解一下这样配置的一点点更细节的过程.其实配置项目属性的时候,有两种方式: 一.通过项目属性管理器配置项目属性表 ...

  8. BZOJ 3039: 玉蟾宫

    3039: 玉蟾宫 Description 有一天,小猫rainbow和freda来到了湘西张家界的天门山玉蟾宫,玉蟾宫宫主蓝兔盛情地款待了它们,并赐予它们一片土地. 这片土地被分成N*M个格子,每个 ...

  9. BNU Invading system

    http://www.bnuoj.com/bnuoj/problem_show.php?pid=29364 这个题被坑了. 题意:密码就是那些数字里面的数,转换成二进制后1最少的那个数,当1的个数相同 ...

  10. android应用如何启动另外一个apk应用

    在开发的过程中,经常会遇到在一个应用中启动另外一个apk应用的情况 问题的核心点在于我们要拿到第三方apk的package名称跟class名称, 如:package名称是com.funcity.tax ...