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常用代码
Nov 23 PHP
php 读取文件乱码问题
Feb 20 PHP
PHP查询数据库中满足条件的记录条数(两种实现方法)
Jan 29 PHP
基于php在各种web服务器的运行模式详解
Jun 03 PHP
使用PHP获取汉字的拼音(全部与首字母)
Jun 27 PHP
php获取发送给用户的header信息的方法
Mar 16 PHP
php提交post数组参数实例分析
Dec 17 PHP
PHP获取昨天、今天及明天日期的方法
Feb 03 PHP
CodeIgniter连贯操作的底层原理分析
May 17 PHP
CI框架简单邮件发送类实例
May 18 PHP
PHP常用的三种设计模式汇总
Aug 28 PHP
Laravel框架下载,安装及路由操作图文详解
Dec 04 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读取ACCESS数据到MYSQL的代码
2011/05/11 PHP
PHP中遇到BOM、编码导致json_decode函数无法解析问题
2014/07/02 PHP
ThinkPHP V2.2说明文档没有说明的那些事实例小结
2015/07/01 PHP
Yii2使用$this-&gt;context获取当前的Module、Controller(控制器)、Action等
2017/03/29 PHP
JavaScript程序员应该知道的45个实用技巧
2014/03/04 Javascript
JavaScript基于setTimeout实现计数的方法
2015/05/08 Javascript
JQuery中基础过滤选择器用法实例分析
2015/05/18 Javascript
Bootstrap入门书籍之(四)菜单、按钮及导航
2016/02/17 Javascript
AngularJS上拉加载问题解决方法
2016/05/23 Javascript
JS传值出现中文参数乱码的解决方法
2016/06/30 Javascript
jq stop()和:is(:animated)的用法及区别(详解)
2017/02/12 Javascript
canvas实现钟表效果
2017/02/13 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
js如何编写简单的ajax方法库
2017/08/02 Javascript
关于JavaScript的单双引号嵌套问题
2017/08/20 Javascript
Vue网页html转换PDF(最低兼容ie10)的思路详解
2017/08/24 Javascript
基于input动态模糊查询的实现方法
2017/12/12 Javascript
详解Python 模拟实现生产者消费者模式的实例
2017/08/10 Python
Python 获取项目根路径的代码
2019/09/27 Python
Python实现不规则图形填充的思路
2020/02/02 Python
深入理解Tensorflow中的masking和padding
2020/02/24 Python
利用Python将多张图片合成视频的实现
2020/11/23 Python
美国最大的购物网站:Amazon.com(亚马逊美国)
2020/05/23 全球购物
Linux面试经常问的文件系统操作命令
2016/10/04 面试题
几个Shell Script面试题
2012/08/31 面试题
你在项目中用到了xml技术的哪些方面?如何实现的?
2014/01/26 面试题
个人收入证明范本
2014/01/12 职场文书
学生生病请假条范文
2014/02/16 职场文书
初一新生军训方案
2014/05/22 职场文书
会计专业毕业生求职信
2014/07/04 职场文书
出生医学证明书
2014/09/15 职场文书
2014年社区党建工作汇报材料
2014/11/02 职场文书
2014年护理工作总结范文
2014/11/14 职场文书
详解如何使用Node.js实现热重载页面
2021/05/06 Javascript
Python 可迭代对象 iterable的具体使用
2021/08/07 Python
Mysql如何查看是否使用到索引
2022/12/24 MySQL