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 相关文章推荐
Search File Contents PHP 搜索目录文本内容的代码
Feb 21 PHP
使用openssl实现rsa非对称加密算法示例
Jan 24 PHP
从零开始学YII2框架(二)通过 Composer 安装扩展插件
Aug 20 PHP
php cookie中点号(句号)自动转为下划线问题
Oct 21 PHP
php文件上传简单实现方法
Jan 24 PHP
PHP开发制作一个简单的活动日程表Calendar
Jun 20 PHP
Mac版PhpStorm之XAMPP整合apache服务器配置的图文教程详解
Oct 13 PHP
phpcms中的评论样式修改方法
Oct 21 PHP
php curl 模拟登录并获取数据实例详解
Dec 22 PHP
微信公众平台开发教程③ PHP实现微信公众号支付功能图文详解
Apr 10 PHP
laravel请求参数校验方法
Oct 10 PHP
详解Laravel框架的依赖注入功能
May 27 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
PHP开发文件系统实例讲解
2006/10/09 PHP
main.php
2006/12/09 PHP
PHP Curl出现403错误的解决办法
2014/05/29 PHP
Lumen timezone 时区设置方法(慢了8个小时)
2018/01/20 PHP
对YUI扩展的Gird组件 Part-2
2007/03/10 Javascript
js getElementsByTagName的简写方式
2010/06/27 Javascript
js循环改变div颜色具体方法
2013/06/25 Javascript
JavaScript中的闭包
2016/02/24 Javascript
JavaScript面向对象程序设计教程
2016/03/29 Javascript
利用Mongoose让JSON数据直接插入或更新到MongoDB
2017/05/03 Javascript
vue2.0的contextmenu右键弹出菜单的实例代码
2017/07/24 Javascript
Angular 2 利用Router事件和Title实现动态页面标题的方法
2017/08/23 Javascript
基于vue1和vue2获取dom元素的方法
2018/03/17 Javascript
vue弹窗插件实战代码
2018/09/08 Javascript
js实现超级玛丽小游戏
2020/03/18 Javascript
vue 重塑数组之修改数组指定index的值操作
2020/08/09 Javascript
原生JavaScript实现换肤
2021/02/19 Javascript
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
10种检测Python程序运行时间、CPU和内存占用的方法
2015/04/01 Python
python线程、进程和协程详解
2016/07/19 Python
解决python3爬虫无法显示中文的问题
2018/04/12 Python
python实现从本地摄像头和网络摄像头截取图片功能
2019/07/11 Python
Python爬虫 bilibili视频弹幕提取过程详解
2019/07/31 Python
python getpass实现密文实例详解
2019/09/24 Python
解决springboot yml配置 logging.level 报错问题
2020/02/21 Python
Python使用进程Process模块管理资源
2020/03/05 Python
Python之字典添加元素的几种方法
2020/09/30 Python
美国环保婴儿用品公司:The Honest Company
2017/11/23 全球购物
Android interview questions
2016/12/25 面试题
关于工作经历的证明书
2014/10/11 职场文书
乐山大佛导游词
2015/02/02 职场文书
开会迟到检讨书范文
2015/05/06 职场文书
年会主持人开场白台词
2015/05/29 职场文书
创业计划书之网络外卖
2019/10/31 职场文书
Python Pandas数据分析之iloc和loc的用法详解
2021/11/11 Python
JS前端监控采集用户行为的N种姿势
2022/07/23 Javascript