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 相关文章推荐
模拟SQLSERVER的两个函数:dateadd(),datediff()
Oct 09 PHP
PHP执行linux系统命令的常用函数使用说明
Apr 27 PHP
php通过数组实现多条件查询实现方法(字符串分割)
May 06 PHP
ThinkPHP采用GET方式获取中文参数查询无结果的解决方法
Jun 26 PHP
PHP关于htmlspecialchars、strip_tags、addslashes的解释
Jul 04 PHP
php调用新浪短链接API的方法
Nov 08 PHP
php防止sql注入简单分析
Mar 18 PHP
PHP Yaf框架的简单安装使用教程(推荐)
Jun 08 PHP
Ajax和PHP正则表达式验证表单及验证码
Sep 24 PHP
Laravel 模型关联基础教程详解
Sep 17 PHP
laravel框架中视图的基本使用方法分析
Nov 23 PHP
PHP vsprintf()函数格式化字符串操作原理解析
Jul 14 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操作MongoDB GridFS 存储文件的详解
2013/06/20 PHP
Laravel5.5 手动分页和自定义分页样式的简单实现
2019/10/15 PHP
JavaScript写的一个自定义弹出式对话框代码
2010/01/17 Javascript
jQuery中的read和JavaScript中的onload函数的区别
2014/08/27 Javascript
js防止页面被iframe调用的方法
2014/10/30 Javascript
jQuery中ajax的get()方法用法实例
2014/12/26 Javascript
十分钟带你快速了解React16新特性
2017/11/10 Javascript
简述vue中的config配置
2018/01/23 Javascript
AngularJS实现的自定义过滤器简单示例
2019/02/02 Javascript
Vue分页器实现原理详解
2019/06/28 Javascript
JavaScript 异步时序问题
2020/11/20 Javascript
python切换hosts文件代码示例
2013/12/31 Python
python实现统计代码行数的方法
2015/05/22 Python
举例讲解Python面向对象编程中类的继承
2016/06/17 Python
Python文件夹与文件的相关操作(推荐)
2016/07/25 Python
详解python异步编程之asyncio(百万并发)
2018/07/07 Python
Python中 map()函数的用法详解
2018/07/10 Python
Win10下python3.5和python2.7环境变量配置教程
2018/09/18 Python
python使用pdfminer解析pdf文件的方法示例
2018/12/20 Python
tensor和numpy的互相转换的实现示例
2019/08/02 Python
Python操作SQLite/MySQL/LMDB数据库的方法
2019/11/07 Python
pycharm通过ssh连接远程服务器教程
2020/02/12 Python
英国健康和美容技术产品购物网站:CurrentBody
2019/07/17 全球购物
常用UNIX 命令(Linux的常用命令)
2015/12/26 面试题
初二物理教学反思
2014/01/29 职场文书
合同协议书格式
2014/04/18 职场文书
合同和协议有什么区别?
2014/10/08 职场文书
个人公司授权委托书范本
2014/10/12 职场文书
先进事迹材料范文
2014/12/29 职场文书
社区国庆节活动总结
2015/03/23 职场文书
幼儿园园长安全责任书
2015/05/08 职场文书
2015年公务员试用期工作总结
2015/05/28 职场文书
教师读书活动心得体会
2016/01/14 职场文书
关于保护环境的建议书
2019/06/24 职场文书
干货:企业内部人才推荐奖励方案!
2019/07/09 职场文书
致创业您:正能量激励人心句子(48条)
2019/08/15 职场文书