PHP设计模式(四)原型模式Prototype实例详解【创建型】


Posted in PHP onMay 02, 2020

本文实例讲述了PHP设计模式:原型模式Prototype。分享给大家供大家参考,具体如下:

1.   概述

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

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

例子2:寄个快递

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

2. 问题

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

3. 解决方案

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

4. 适用性

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

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

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

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

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

5. 结构

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

PHP设计模式(四)原型模式Prototype实例详解【创建型】

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. 浅拷贝和深拷贝

原型模式的原理图:

PHP设计模式(四)原型模式Prototype实例详解【创建型】

浅拷贝

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

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

PHP设计模式(四)原型模式Prototype实例详解【创建型】

深拷贝

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

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

PHP设计模式(四)原型模式Prototype实例详解【创建型】

深拷贝要深入到多少层,是一个不确定的问题。

在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。

因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。

10. 带Prototype Manager的原型模式

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

PHP设计模式(四)原型模式Prototype实例详解【创建型】

       原型管理器(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();
?>

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php合并数组array_merge函数运算符加号与的区别
Oct 31 PHP
PHP文件读写操作相关函数总结
Nov 18 PHP
Laravel使用Caching缓存数据减轻数据库查询压力的方法
Mar 15 PHP
thinkPHP基于ajax实现的菜单与分页示例
Jul 12 PHP
基于PHPexecl类生成复杂的报表表头示例
Oct 14 PHP
php实现的http请求封装示例
Nov 08 PHP
详解Yii2高级版引入bootstrap.js的一个办法
Mar 21 PHP
PHP ADODB生成HTML表格函数rs2html功能【附错误处理函数用法】
May 29 PHP
PHP封装的验证码工具类定义与用法示例
Aug 22 PHP
JS(jQuery)实现聊天接收到消息语言自动提醒功能详解【提示“您有新的消息请注意查收”】
Apr 16 PHP
laravel框架模型和数据库基础操作实例详解
Jan 25 PHP
php ActiveMQ的安装与使用方法图文教程
Feb 23 PHP
PHP设计模式(三)建造者模式Builder实例详解【创建型】
May 02 #PHP
PHP设计模式(一)工厂模式Factory实例详解【创建型】
May 02 #PHP
PHP设计模式概论【概念、分类、原则等】
May 01 #PHP
PHP设计模式之 策略模式Strategy详解【对象行为型】
May 01 #PHP
php如何获取Http请求
Apr 30 #PHP
PHP 命名空间和自动加载原理与用法实例分析
Apr 29 #PHP
Yii使用EasyWechat实现小程序获取用户的openID的方法
Apr 29 #PHP
You might like
用PHP和ACCESS写聊天室(六)
2006/10/09 PHP
解析php中var_dump,var_export,print_r三个函数的区别
2013/06/21 PHP
php三维数组去重(示例代码)
2013/11/26 PHP
一个PHP的ZIP压缩类分享
2014/05/04 PHP
浅析php单例模式
2014/11/25 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
基于PHP实现的多元线性回归模拟曲线算法
2018/01/30 PHP
Laravel如何使用Redis共享Session
2018/02/23 PHP
不能再简单的无闪刷新验证码原理很简单
2007/11/05 Javascript
js prototype截取字符串函数
2010/04/01 Javascript
javascript之学会吝啬 精简代码
2010/04/25 Javascript
网页右下角弹出窗体实现代码
2014/06/05 Javascript
Javascript 实现简单计算器实例代码
2016/10/23 Javascript
JS数组去重的6种方法完整实例
2018/12/08 Javascript
详解微信小程序图片地扯转base64解决方案
2019/08/18 Javascript
[33:09]完美世界DOTA2联赛循环赛 Forest vs DM BO2第二场 10.29
2020/10/29 DOTA
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
简单谈谈Python中的json与pickle
2017/07/19 Python
浅谈用Python实现一个大数据搜索引擎
2017/11/28 Python
利用python和百度地图API实现数据地图标注的方法
2019/05/13 Python
python判断单向链表是否包括环,若包含则计算环入口的节点实例分析
2019/10/23 Python
Python变量及数据类型用法原理汇总
2020/08/06 Python
Python实现上下文管理器的方法
2020/08/07 Python
python代码实现图书管理系统
2020/11/30 Python
SmartBuyGlasses德国:购买太阳镜和眼镜
2019/08/20 全球购物
求职自荐信范文格式
2013/11/29 职场文书
八年级历史教学反思
2014/01/10 职场文书
电子商务专业求职信
2014/03/08 职场文书
关于梦想的演讲稿
2014/05/05 职场文书
上诉答辩状范文
2015/05/22 职场文书
计算机实训心得体会
2016/01/14 职场文书
Nginx+Tomcat实现负载均衡、动静分离的原理解析
2021/03/31 Servers
【DOTA2】总决赛血虐~ XTREME GAMING vs MAGMA - OGA DOTA PIT 2022 CN
2022/04/02 DOTA
mysql sql常用语句大全
2022/06/21 MySQL
详解apache编译安装httpd-2.4.54及三种风格的init程序特点和区别
2022/07/15 Servers
win10蓝屏0xc0000001安全模式进不了怎么办?win10出现0xc0000001的解决方法
2022/08/05 数码科技