PHP设计模式之原型模式定义与用法详解


Posted in PHP onApril 03, 2018

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

原型设计模式(Prototype Design Pattern)很有意思, 因为它使用了一种克隆技术来复制实例化的对象. 新对象是通过复制原型实例来创建的. 在这里, 实例是批实例化的具体类.原型设计模式的目的是通过使用克隆来减少实例化对象的开销.与其从一个类实例化新对象, 完全可以使用一个已有实例的克隆.

克隆函数

PHP中使用原型设计模式的关键是要了解如何使用内置函数__clone().

<?php
abstract class CloneObject
{
  public $name;
  public $picture;
  abstract function __clone();
}
class Boy extends CloneObject
{
  public function __construct()
  {
    $this->face = "handsome";
    $this->name = "chenqionghe";
  }
  public function display()
  {
    echo 'look : '.$this->face;;
    echo '<br />'.$this->name.'<br />';
  }
  public function __clone() {}
}
$boy = new Boy();
$boy->display();
$cloneBoy = clone $boy;
$cloneBoy->face = "still handsome";
$cloneBoy->display();

运行结果如下

look : handsome
chenqionghe
look : still handsome
chenqionghe

$cloneBoy实例是通过克隆Boy的实例$boy, 它可以像$boy一样访问相同的属性, 而且像Boy类的直接实例一样改变这些属性.

注意: 对于所克隆的实例 , clone关键字会为该实例的类实例化另一个实例(使用克隆关键字可以创建一个类的副本, 如果可能, 会自动调用对象的__clone方法, 但不能直接调用 对象的__clone方法), 关于过程, 有一点需要注意的是, 克隆不会不会启动构造函数中的动作.

简单的原型例子

我们以研究果蝇为例.研究的目标是建立一个原型果蝇, 然后一旦出现变异, 就构建这个变异果蝇

抽象类接口和具体实现

原型(IPrototype)的两个具体类实现分别表示不同性别的果蝇, 包括性别变量(gender)和不同性别的和行为.

IPrototype.php

<?php
abstract class IPrototype
{
  public $eyeColor;
  public $winBeat;
  public $unitEypes;
  abstract function __clone();
}

IPrototype的这两个实现的区别体现在性别上, 性别用常量标识, 一个是MALE,另一个是FEMAIL.雄果蝇有一个$mated布尔变量, 雄果蝇交配后,这个布尔变量会设置为true, 雌果蝇有一个$fecundity变量,其中包含一个数字值, 表示这只雄果蝇的繁殖能力(产卵个数):

MaleProto.php

<?php
include_once('IPrototype.php');
class MaleProto extends IPrototype
{
  const gender = "MALE";
  public $mated;
  public function __construct()
  {
    $this->eyeColor = "red";
    $this->winBeat = "220";
    $this->unitEypes = "760";
  }
  public function __clone(){}
}

FemaleProto.php

<?php
include_once('IPrototype.php');
class FemaleProto extends IPrototype
{
  const gender = "FEMAIL";
  public $fecundity;
  public function __construct()
  {
    $this->eyeColor = "red";
    $this->winBeat = "220";
    $this->unitEypes = "760";
  }
  public function __clone(){}
}

客户

在原型设计模式中,Clien类确实是一个不可缺少的部分.原因在于, 尽管要将子类具体实现作为实例的模板,但使用相同的模板克隆实例的具体工作是由Client类完成的

Client.php

<?php
function __autoload($class_name)
{
  include_once $class_name.'.php';
}
class Client
{
  //用于直接实例化
  private $fly1;
  private $fly2;
  //用于克隆
  private $c1Fly;
  private $c2Fly;
  private $updatedCloneFly;
  public function __construct()
  {
    //实例化
    $this->fly1 = new MaleProto();
    $this->fly2 = new FemaleProto();
    //克隆
    $this->c1Fly = clone $this->fly1;
    $this->c2Fly = clone $this->fly2;
    $this->updatedCloneFly = clone $this->fly2;
    //更新克隆
    $this->c1Fly->mated = "true";
    $this->c2Fly->fecundity = '186';
    $this->updatedCloneFly->eyeColor = "purple";
    $this->updatedCloneFly->winBeat = "220";
    $this->updatedCloneFly->unitEyes = '750';
    $this->updatedCloneFly->fecundity = '92';
    //通过类型提示方法发送
    $this->showFly($this->c1Fly);
    $this->showFly($this->c2Fly);
    $this->showFly($this->updatedCloneFly);
  }
  private function showFly(IPrototype $fly)
  {
    echo "Eye color: ".$fly->eyeColor."<br />";
    echo "Wing Beats/second: ".$fly->winBeat."<br />";
    echo "Eye units: ".$fly->unitEypes."<br />";
    $genderNow = $fly::gender;
    echo "Gender: ".$genderNow."<br />";
    if($genderNow == "FEMAIL")
    {
      echo "Number of eggs: ".$fly->fecundity."<hr />";
    }
    else
    {
      echo "Mated: ".$fly->mated."<hr />";
    }
  }
}
$worker = new Client();

运行结果如下

Eye color: red
Wing Beats/second: 220
Eye units: 760
Gender: MALE
Mated: trueEye color: red
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 186Eye color: purple
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 92

原型模式要依赖客户通过 不念克隆过程使用具体原型.在这个设计过程中, 客户是完成克隆的参与者, 由于克隆是原型设计中的关键要素, 所以客户是一个基本参与者, 而不仅仅是一个请求类.

现代企业组织

在创建型设计模式方面,现代企业组织就非常适合原型实现.现在企业组织往往是复杂而庞大的层次结构, 像面向对象编程一样,要为很多共同的特征建模.现在通过一个例子描述软件工程公司.

软件工程公司是一个典型的现代组织.工程部(Engineering Department)负责创建产品,管理部(Management)处理资源的协调组织,市场部(Marketing)负责产品的销售,推广和整体营销.

接口中的封装

在这个原型实现中,首先为程序的接口(一个抽象类)增加OOP,与所有原型接口一样,这个接口包含一个克隆操作.另外它还包含一些抽象和具体的获取方法和设置方法.其中有一个抽象获取方法/设置方法对,但要由3个具体原型实现为这个抽象获取方法/设置方法对提供具体实现.其他获取方法和设置方法分分别应用于员工名,ID码和照片等属性.注意所有这些属性都是保护属性(protected),所以尽管具体的获取方法和设置方法有公共可见性,但由于操作中使用的属性具有保护和可见性,这就提供了某种程度的封装:

<?php
abstract class IAcmePrototype
{
  protected $name;
  protected $id;
  protected $employeePic;
  protected $department;
  //部门
  abstract function setDept($orgCode);
  abstract function getDept();
  //名字
  public function setName($emName)
  {
    $this->name = $emName;
  }
  public function getName()
  {
    return $this->name;
  }
  //ID
  public function setId($emId)
  {
    $this->id = $emId;
  }
  public function getId()
  {
    return $this->id;
  }
  //雇员图像
  public function setPic($ePic)
  {
    $this->employeePic = $ePic;
  }
  public function getPic()
  {
    return $this->employeePic;
  }
  abstract function __clone();
}

利用这些获取方法和设置方法, 所有属性的值都通过继承的保护变量来设置.采用这种设计, 扩展类及其实例可以得到更好的封装.

接口实现

3个IAcmePrototype子类都必须实现"dept"抽象方法以及__clone()方法.类似地, 每个具体原型类还包含一个常量UNIT,它提供一个赋值,可以由实例(包括克隆的对象)作为标识

首先来看市场部的Marketing类:

Marketing.php

<?php
include_once('IAcmePrototype.php');
class Marketing extends IAcmePrototype
{
  const UNIT = "Marketing";
  private $sales = "sales";
  private $promotion = "promotion";
  private $strategic = "strategic planning";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 101:
        $this->department = $this->sales;
        break;
      case 102:
        $this->department = $this->promotion;
        break;
      case 103:
        $this->department = $this->strategic;
        break;
      default :
        $this->department = "未识别的市场部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function __clone() {}
}

setDept()方法的实现使用了一个参数.并不是直接输入市场部的部门,这个方法的参数是一个数字码, 通过一个switch语句,限制了3种可接受的情况和默认情况,别外两个原型实现也类似

Management.php

<?php
include_once('IAcmePrototype.php');
class Management extends IAcmePrototype
{
  const UNIT = "Management";
  private $research = "research";
  private $plan = "planning";
  private $operations = "operations";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 201:
        $this->department = $this->research;
        break;
      case 202:
        $this->department = $this->plan;
        break;
      case 203:
        $this->department = $this->operations;
        break;
      default :
        $this->department = "未识别的管理部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function __clone() {}
}

Engineering.php

<?php
include_once('IAcmePrototype.php');
class Engineering extends IAcmePrototype
{
  const UNIT = "Engineering";
  private $development = "development";
  private $design = "design";
  private $sysAd = "system administration";
  public function setDept($orgCode)
  {
    switch($orgCode)
    {
      case 301:
        $this->department = $this->development;
        break;
      case 302:
        $this->department = $this->design;
        break;
      case 303:
        $this->department = $this->sysAd;
        break;
      default :
        $this->department = "未识别的工程部";
    }
  }
  public function getDept()
  {
    return $this->department;
  }
  public function __clone() {}
}

以上这3个具体原型实现分别有其特定用途,不过它们都符合接口,可以创建各个原型实现的一个实例, 然后根据需要克隆多个实例.这个克隆的工作由Client类完成

客户

客户的设置非常简单: 分别创建各个具体原型的一个实例, 然后按以下列表来克隆各个实例:

市场部门实例:
-----市场部门克隆
-----市场部门克隆
管理部门实例
-----管理部门克隆
工程部门实例
-----工程部门克隆
-----工程部门克隆

将来只使用这些克隆对象.使用获取方法和设置方法将各个特定情况的信息赋给这些克隆对象.以下是Client的实现

Client.php

<?php
function __autoload($class_name)
{
  include_once $class_name.'.php';
}
class Client
{
  private $market;
  private $manage;
  private $engineer;
  public function __construct()
  {
    $this->makeConProto();
    $Tess = clone $this->market;
    $this->setEmployee($Tess, "Tess Smith" , 101, 'ts101-1234', 'tess.png');
    $this->showEmployee($Tess);
    $Jacob = clone $this->market;
    $this->setEmployee($Jacob, "Jacob Jones" , 101, 'jj101-2234', 'jacob.png');
    $this->showEmployee($Jacob);
    $Ricky = clone $this->manage;
    $this->setEmployee($Ricky, "Ricky Rodriguez" , 203, 'rr101-5634', 'ricky.png');
    $this->showEmployee($Ricky);
    $Olivaia = clone $this->engineer;
    $this->setEmployee($Olivaia, "Olivaia Perez" , 302, 'op301-1278', 'olivia.png');
    $this->showEmployee($Olivaia);
    $John = clone $this->engineer;
    $this->setEmployee($John, "John Jacson" , 101, 'jj301-14548', 'john.png');
    $this->showEmployee($John);
  }
  private function makeConProto()
  {
    $this->market = new Marketing();
    $this->manage = new Management();
    $this->engineer = new Engineering();
  }
  private function showEmployee(IAcmePrototype $employeeNow)
  {
    $px = $employeeNow->getPic();
    echo "<img src=$px width='150' height='150' /><br />";
    echo $employeeNow->getName().'<br />';
    echo $employeeNow->getDept().':'.$employeeNow::UNIT.'<br />';
    echo $employeeNow->getId().'<hr />';
  }
  private function setEmployee(IAcmePrototype $employeeNow, $nm, $dp, $id, $px)
  {
    $employeeNow->setName($nm);
    $employeeNow->setDept($dp);
    $employeeNow->setId($id);
    $employeeNow->setPic($px);
  }
}
$worker = new Client();

解释:

客户Client的构造函数类包含3个私有属性, 用来分别实例化3个具体原型类. makeConPro()方法生成必要的实例.

接下来,使用克隆技术创建一个"员工"实例.然后,这个实例向一个设置方法setEmployee()发送特定的实例信息,这个设置方法使用IAcmePrototype接口类型提示,不过需要说明, 它只对第一个参数使用类型提示,其他参数都没有类型提示, 并不要求它们派生自IAcmePrototype接口.克隆"员工"可以使用IAcmePrototype抽象类的所有设置方法以及具体原型类实现的setDept()方法.

要使用各个员工的数据,Client类可以使用继承的获取方法.以下是运行Client输出的结果

Tess Smith
sales:Marketing
ts101-1234
Jacob Jones
sales:Marketing
jj101-2234
Ricky Rodriguez
operations:Management
rr101-5634
Olivaia Perez
design:Engineering
op301-1278
John Jacson
未识别的工程部:Engineering
jj301-14548

可以根据需要增加更多的克隆, 而且只需要对具体原型类完成一次实例化.使用原型模式时, 并不是建立具体类的多个实例,而只需要一个类实例化和多个克隆.

完成修改,增加特性

要记住,最重要(可能也是最基本)的是, 设计模式允许开发人员修改和增补程序,而不必一切从头开始.例如, 假设总裁决定公司增加一个新的部门,比如研究部门(Research), 这会很难吗?一点也不难.Research可以扩展IAcmePrototype抽象类, 然后实现抽象获取方法和设置方法来反映这个研究部门的组织.需要说明,Client类中获取方法和设置方法使用的代码提示指示一个接口,而不是一个抽象类的具体实现.所以, 只要增加的单元正确地实现了这个接口,就能顺利地增加到应用中, 而不会带来波动,也不需要对程序中的其他参与者进行重构.

不仅可以增加更多的具体类, 还可以很容易地对各个类进行修改, 而不会造成破坏.例如假设这个组织的市场部决定,除了现有的部门外, 他们还需要一个特殊的在线市场部,. 这样一来, switch/case操作需要一个新的分支(case), 还要有一个新的私有属性(变量)来描述新增的这个部门.这个改变将封冻在单独的类中, 而不会影响其他参与者.由于这种改变不会带来破坏, 所以应用的规模越大, 这一点就越重要.可以看到原型设计模式不仅支持一致性, 还支持灵活的改变.

PHP世界中的原型

由于PHP是一个服务器端语言,也是与MySQL数据库交互的一个重要工具,所以原型设计模式尤其适用 .并不是为数据库的第一个元素分别创建对象,PHP可以使用原型模式创建具体类的一个实例,然后利用数据库中的数据克隆其余的实例(记录).

了解克隆过程之后,与直接实例化的过程相比,你可能会问:"这有什么区别?" 换句话说,为什么克隆比直接实例化对象需要的资源少?它们的区别并不能直接看到. 一个对象通过克隆创建实例时, 它不会启动构造函数.克隆能得到原始类的所有属性, 甚至还包含父接口的属性,另外还继承了传递实例化对象的所有值.构造函数生成的所有值以及存储在对象属性中的值都会成为克隆对象的一部分.所以没有返回构造函数.如果发现你的克隆对象确实需要访问构造函数生成的值但又无法访问, 这说明需要对类进行重构,使实例能拥有它们需要的一切信息, 而且可以把这些数据传递给克隆对象.

总的来说, 原型模式在很多不同类型的PHP项目中都很适用, 如果解决一个问题需要乃至创建型模式, 都可以使用原型模式.

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

PHP 相关文章推荐
PHP 变量的定义方法
Jan 26 PHP
PHP strtok()函数的优点分析
Mar 02 PHP
有关phpmailer的详细介绍及使用方法
Jan 28 PHP
PHP中的函数-- foreach()的用法详解
Jun 24 PHP
php获取数组长度的方法(有实例)
Oct 27 PHP
php导出csv格式数据并将数字转换成文本的思路以及代码分享
Jun 05 PHP
yii去掉必填项中星号的方法
Dec 28 PHP
Zend Framework教程之Loader以及PluginLoader用法详解
Mar 09 PHP
thinkPHP使用pclzip打包备份mysql数据库的方法
Apr 30 PHP
PHP魔术方法以及关于独立实例与相连实例的全面讲解
Oct 18 PHP
PHP实现在对象之外访问其私有属性private及保护属性protected的方法
Nov 20 PHP
Mac M1安装mnmp (Mac+Nginx+MySQL+PHP) 开发环境
Mar 29 PHP
thinkPHP框架自动填充原理与用法分析
Apr 03 #PHP
PHP设计模式之适配器模式定义与用法详解
Apr 03 #PHP
PHP延迟静态绑定的深入讲解
Apr 02 #PHP
PHP设计模式之装饰器模式定义与用法详解
Apr 02 #PHP
PHP设计模式之状态模式定义与用法详解
Apr 02 #PHP
PHP设计模式之模板方法模式定义与用法详解
Apr 02 #PHP
PHP实现动态获取函数参数的方法示例
Apr 02 #PHP
You might like
实用函数4
2007/11/08 PHP
PHP HTML代码串截取代码
2008/12/29 PHP
理解和运用PHP中的多态性[译]
2011/08/02 PHP
setcookie中Cannot modify header information-headers already sent by错误的解决方法详解
2013/05/08 PHP
Laravel4中的Validator验证扩展用法详解
2016/07/26 PHP
PHP生成随机数的方法总结
2018/03/01 PHP
javascript prototype,executing,context,closure
2008/12/24 Javascript
JavaScript DOM 添加事件
2009/02/14 Javascript
xml文档转换工具,附图表例子(hta)
2010/11/17 Javascript
怎么选择Javascript框架(Javascript Framework)
2013/11/22 Javascript
js验证整数加保留小数点的简单实例
2013/12/02 Javascript
使用firebug进行调试javascript的示例
2013/12/16 Javascript
js实现无限级树形导航列表效果代码
2015/09/23 Javascript
jQuery 3 中的新增功能汇总介绍
2016/06/12 Javascript
AngularJS 模型详细介绍及实例代码
2016/07/27 Javascript
基于JavaScript实现右键菜单和拖拽功能
2016/11/28 Javascript
vue.js实现单选框、复选框和下拉框示例
2017/07/18 Javascript
js 获取json数组里面数组的长度实例
2017/10/31 Javascript
three.js实现3D影院的原理的代码分析
2017/12/18 Javascript
vue 表单输入格式化中文输入法异常问题
2018/05/30 Javascript
vue v-model实现自定义样式多选与单选功能
2018/07/05 Javascript
JavaScript实现图片伪异步上传过程解析
2020/04/10 Javascript
基于Echarts图表在div动态切换时不显示的解决方式
2020/07/20 Javascript
Python实现遍历windows所有窗口并输出窗口标题的方法
2015/03/13 Python
python中Switch/Case实现的示例代码
2017/11/09 Python
对tf.reduce_sum tensorflow维度上的操作详解
2018/07/26 Python
Python中的集合介绍
2019/01/28 Python
基于python3 的百度图片下载器的实现代码
2019/11/05 Python
python dict乱码如何解决
2020/06/07 Python
Python关于拓扑排序知识点讲解
2021/01/04 Python
DogBuddy荷兰:找到你最完美的狗保姆
2019/04/17 全球购物
澳大利亚最受欢迎的美发用品目的地:AMR
2019/08/28 全球购物
毕业生自我鉴定范文
2013/11/08 职场文书
阳光体育活动总结
2014/04/30 职场文书
党员四风问题对照检查材料思想汇报
2014/09/16 职场文书
再见,2019我们不负使命;你好,2020我们砥砺前行
2020/01/03 职场文书