php示例详解Constructor Prototype Pattern 原型模式


Posted in PHP onOctober 15, 2015

原型模式中主要角色

抽象原型(Prototype)角色:声明一个克隆自己的接口
具体原型(Concrete Prototype)角色:实现一个克隆自己的操作

当一个类大部分都是相同的只有部分是不同的时候,如果需要大量这个类的对象,每次都重复实例化那些相同的部分是开销很大的,而如果clone之前建立对象的那些相同的部分,就可以节约开销。

针对php的一种实现方式就是__construct()和initialize函数分开分别处理这个类的初始化,construct里面放prototype也就是公共的部分,initialize里面是每个对象特殊的部分。这样我们先建立一个类不initialize,以后每次clone这个类再进行initialize就可以了。

 在zend framework官方手册里面提到了这个http://framework.zend.com/manual/2.0/en/user-guide/database-and-models.html,但是没有细讲,下面我来分析一下

一、引入

在zf2的model里面有一个albumTable类,相当于一个操作数据库动作的助手类,里面用到了tablegateway。

为了每次初始化albumtable都是相同的一个类,将初始化工作放到了根目录的module.php文件的getServiceConfig(),其中用到工厂模式,并且通过回调函数,当每次ServiceManager($sm)需要实例化一个对象的时候会自动调用创建一个alumTable。下面代码我们可以看出,创建一个albumTable还需要用相同的方式创建一个AlbumTableGateWay,这个类就用到了我们所要讲的原型模式。

二、代码详解

public function getServiceConfig()
  {
    return array(
      'factories' => array(
        'Album\Model\AlbumTable' => function($sm) {
          $tableGateway = $sm->get('AlbumTableGateway');
          $table = new AlbumTable($tableGateway);
          return $table;
        },
        'AlbumTableGateway' => function ($sm) {
          $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
          $resultSetPrototype = new ResultSet();
          $resultSetPrototype->setArrayObjectPrototype(new Album());//这个就是一个不变的原型
          return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);//传入到TableGateWay的构造函数中去
        },
      ),
    );
  }

注意并不是TableGateWay运用了原型模式而是ResultSet这个类运用了。每当tablegateway调用select()或者insert()等方法的时候都会建立一个ResultSet用来表示结果,这些ResultSet中公共部分被clone,而独特的部分类如data就会被initialize。

三、更多代码示例

为了更清晰得了解这个原型,我们先抛开zend这个大框架,看一个完整的代码示例。示例来自

<a href="http://ralphschindler.com/2012/03/09/php-constructor-best-practices-and-the-prototype-pattern">PHP Constructor Best Practices And The Prototype Pattern</a>

这篇文章关于prototype pattern的部分前半部分其实是混杂怎样在构造函数中运用继承来提高扩展性,两个模式看起来可能不太好理解,我们直接看最后的代码关于prototype pattern的部分。

<?php
//框架中很常见的adapter类,用来适配各种数据库,封装一些基本数据库连接操作。
//相当于上面代码中的adapter类
class DbAdapter {
  public function fetchAllFromTable($table) {
    return $arrayOfData;
  }
}
//运用prototype pattern的类,注意construct和initialize是分开的
//相当于上面zend 代码里面的ResultSet类
class RowGateway {
  public function __construct(DbAdapter $dbAdapter, $tableName) {
    $this->dbAdapter = $dbAdapter;
    $this->tableName = $tableName;
  }
  public function initialize($data) {
    $this->data = $data;
  }
  /**
   * Both methods require access to the database adapter
   * to fulfill their duties
   */
  public function save() {}
  public function delete() {}
  public function refresh() {}
}
//相当于上面代码中的TableGateway类,关于gateway可以具体去了解一下。
class UserRepository {
  public function __construct(DbAdapter $dbAdapter, RowGateway $rowGatewayPrototype = null) {
    $this->dbAdapter = $dbAdapter;
    $this->rowGatewayPrototype = ($rowGatewayPrototype) ? new RowGateway($this->dbAdapter, 'user')
  }
  public function getUsers() {
    $rows = array();
    foreach ($this->dbAdapter->fetchAllFromTable('user') as $rowData) {
      $rows[] = $row = clone $this->rowGatewayPrototype;
      $row->initialize($rowData);
    }
    return $rows;
  }
}

这几个类其实和上面zend代码中的类是对应的

Dbadapter -- adpater

RowGateWay -- ResultSet

UserRepository - TableGateWay

具体看代码中的注释。

这里的RowGateWay可以很明显的看出在getusers中需要大量的实例化,那么原型模式就是很必要的了。

下面是运用这个类的代码

class ReadWriteRowGateway extends RowGateway {
  public function __construct(DbAdapter $readDbAdapter, DbAdapter $writeDbAdapter, $tableName) {
    $this->readDbAdapter = $readDbAdapter;
    parent::__construct($writeDbAdapter, $tableName);
  }
  public function refresh() {
    // utilize $this->readDbAdapter instead of $this->dbAdapter in RowGateway base implementation
  }
}
// usage:
$userRepository = new UserRepository(
  $dbAdapter,
  new ReadWriteRowGateway($readDbAdapter, $writeDbAdapter, 'user')
);
$users = $userRepository->getUsers();
$user = $users[0]; // instance of ReadWriteRowGateway with a specific row of data from the db

以上内容是小编给大家介绍的php示例详解Constructor Prototype Pattern 原型模式,希望大家喜欢。

PHP 相关文章推荐
将PHP作为Shell脚本语言使用
Oct 09 PHP
用PHP+MySql编写聊天室
Oct 09 PHP
弄了个检测传输的参数是否为数字的Function
Dec 06 PHP
基于HTTP长连接的&quot;服务器推&quot;技术的php 简易聊天室
Oct 31 PHP
php 备份数据库代码(生成word,excel,json,xml,sql)
Jun 23 PHP
php中利用explode函数分割字符串到数组
Feb 08 PHP
PHP内存缓存Memcached类实例
Dec 08 PHP
php中使用base HTTP验证的方法
Apr 20 PHP
PHP读取、解析eml文件及生成网页的方法示例
Sep 04 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
Feb 19 PHP
PHP使用file_get_contents发送http请求功能简单示例
Apr 29 PHP
PHP实现八皇后算法
May 06 PHP
PHP经典面试题之设计模式(经常遇到)
Oct 15 #PHP
PHP面试题之文件目录操作
Oct 15 #PHP
php cli配置文件问题分析
Oct 15 #PHP
PHP+Mysql+jQuery实现发布微博程序 php篇
Oct 15 #PHP
10个php函数实用却不常见
Oct 13 #PHP
PHP实现连接设备、通讯和发送命令的方法
Oct 13 #PHP
PHP如何通过传引用的思想实现无限分类(代码简单)
Oct 13 #PHP
You might like
PHP cdata 处理(详细介绍)
2013/07/05 PHP
PHP中你应该知道的require()文件包含的正确用法
2015/06/12 PHP
10个值得深思的PHP面试题
2016/11/14 PHP
PHP7扩展开发之基于函数方式使用lib库的方法详解
2018/01/15 PHP
ThinkPHP3.1.2 使用cli命令行模式运行的方法
2020/04/14 PHP
php中yii框架实例用法
2020/12/22 PHP
FireFox JavaScript全局Event对象
2009/06/14 Javascript
一款Jquery 分页插件的改造方法(服务器端分页)
2011/07/11 Javascript
关于jquery css的使用介绍
2013/04/18 Javascript
Javascript的setTimeout()使用闭包特性时需要注意的问题
2014/09/23 Javascript
JS实现具备延时功能的滑动门菜单效果
2015/09/17 Javascript
详解JavaScript的表达式与运算符
2015/11/30 Javascript
jQuery中bind(),live(),delegate(),on()绑定事件方法实例详解
2016/01/19 Javascript
在node.js中怎么屏蔽掉favicon.ico的请求
2017/03/01 Javascript
jQuery实现三级联动效果
2017/03/02 Javascript
JavaScript你不知道的一些数组方法
2017/08/18 Javascript
react配置antd按需加载的使用
2019/02/11 Javascript
Javascript异步流程控制之串行执行详解
2020/09/27 Javascript
[04:10]2016国际邀请赛中国区预选赛第二日TOP10精彩集锦
2016/06/28 DOTA
Python实现读取目录所有文件的文件名并保存到txt文件代码
2014/11/22 Python
windows 下python+numpy安装实用教程
2017/12/23 Python
python统计多维数组的行数和列数实例
2018/06/23 Python
python实现beta分布概率密度函数的方法
2019/07/08 Python
python实现的config文件读写功能示例
2019/09/24 Python
详解Python IO编程
2020/07/24 Python
Lacoste澳大利亚官网:服装、鞋类及配饰
2018/11/14 全球购物
俄罗斯厨房产品购物网站:COOK HOUSE
2021/03/15 全球购物
日语求职信范文
2013/12/17 职场文书
好的演讲稿开场白
2013/12/30 职场文书
学生自我评价范文
2014/02/02 职场文书
幼儿园安全责任书范本
2014/07/24 职场文书
民事撤诉申请书范本
2015/05/18 职场文书
单位收入证明范本
2015/06/18 职场文书
高中运动会前导词
2015/07/20 职场文书
MySQL中VARCHAR与CHAR格式数据的区别
2021/05/26 MySQL
Redis 彻底禁用RDB持久化操作
2021/07/09 Redis