Zend Framework中的简单工厂模式 图文


Posted in PHP onJuly 10, 2012

前段时间用来ZF,把他当黑盒感觉不是很好,一直有看其源码的冲动,但是。。。如果一点一点点看的话,挑战确实有些大了。某天又然后想到好久没复习设计模式了。综合一下,复习一个设计模式之后在ZF中找一下使用这模式的源码吧,不读所有源码,读读比较”高级”的部分吧,要说模式,暂时不知道是不是所有模式ZF里面都有,但是应该有足够的模式够我最近看了,在说可以找找其他开源的软件来找模式。这段时间被各种笔试神马乱七八糟的把生活搞得稍微有点乱,但是不管怎样,复习还是必须的吧。再说一下ZF吧,ZF一个好处就是各个component比较独立,component之间没有过多的依赖,这样一来,为使用者提供了方便,当然也为我这样无聊且懒的想看源码的人提供了方便。

今天看看简单工厂,ZF里面不缺模式,更不缺工厂模式,大名鼎鼎的的 Zend_Db就毫不吝啬的使用简单工厂,再ctrl+h(zend studio下)一下会发现factory特别多,如果没猜错应该大多应该也是简单工厂。由于Zend_Db 最常用,我也就自然的会比较想看一下他的实现。在查看源码之前先复习一下怎么用Zend_Db和简单工厂(这里是一个stack,先复习简单工厂)。

复习简单工厂模式
用类图回忆一下,简单工厂类图:

Zend Framework中的简单工厂模式 图文

借用《研磨设计模式》作者的一张图,可以看到Client通过factory来获取对象,通过Api结构来调用。用factory把具体的Api的创建隐藏起来。而其他所有使用者在使用时,只需要知道用factory创建,通过Api结构调用即可,简单复习完成。看到类图应该能想起简单工厂了,因为他本身确实很简单。复习完简单工厂,思维稍微跳跃一下,直接来看看Zend_Db的使用。
1.复习Zend_Db的使用
如果不知道如何使用,准备看XXX的源码却不知道怎么用XXX,这有点?澹??韵刃⌒〉目匆幌?end_Db的使用,下面这段是在ZF官方文档里面的(个人不是很喜欢ZF文档,没Yii易读)
/public/index.php
$db = Zend_Db::factory('Pdo_Mysql', array( 
'host' => '127.0.0.1', 
'username' => 'webuser', 
'password' => 'xxxxxxxx', 
'dbname' => 'test' 
));

这里是把数据库配置也放到代码里面,看起来最简单(实际上其他也不难,只是数据库放置的位置不同,便于管理罢了),但这样在正常情况下不是最好的方式,但是为了突出重点,这里选用这中最简单的方式。注意里面的Zend_Db::factory(‘Pdo_Mysql'…这段
上面生成了一个$db(一个Zend_Db对象),使用上面的$db进行查询如下:
$db->setFetchMode(Zend_Db::FETCH_OBJ); 
$result = $db->fetchAssoc( 
'SELECT bug_id, bug_description, bug_status FROM bugs' 
);

继续来自官网文档,这是取记录的模式为Object,再fetch,一切目前看起来都自然而然,但是至今还是把它Zend_Db当作一个黑盒使用。下面可以进入正题。
首先,查看一下zend/Db.php的代码摘要:
< ?php 
class Zend_Db 
{ 
/** 
设定一些常量和默认值 
*/ 
/** 
* Factory for Zend_Db_Adapter_Abstract classes. 
* 
* First argument may be a string containing the base of the adapter class 
* name, e.g. 'Mysqli' corresponds to class Zend_Db_Adapter_Mysqli. This 
* name is currently case-insensitive, but is not ideal to rely on this behavior. 
* If your class is named 'My_Company_Pdo_Mysql', where 'My_Company' is the namespace 
* and 'Pdo_Mysql' is the adapter name, it is best to use the name exactly as it 
* is defined in the class. This will ensure proper use of the factory API. 
* 
* First argument may alternatively be an object of type Zend_Config. 
* The adapter class base name is read from the 'adapter' property. 
* The adapter config parameters are read from the 'params' property. 
* 
* Second argument is optional and may be an associative array of key-value 
* pairs. This is used as the argument to the adapter constructor. 
* 
* If the first argument is of type Zend_Config, it is assumed to contain 
* all parameters, and the second argument is ignored. 
* 
* @param mixed $adapter String name of base adapter class, or Zend_Config object. 
* @param mixed $config OPTIONAL; an array or Zend_Config object with adapter parameters. 
* @return Zend_Db_Adapter_Abstract 
* @throws Zend_Db_Exception 
*/ 
public static function factory ($adapter, $config = array()) 
{ 
//使用Zend_Config对象,上述方式没有使用,直接使用Array 
if ($config instanceof Zend_Config) { 
$config = $config->toArray(); 
} 
/* 
* Convert Zend_Config argument to plain string 
* adapter name and separate config object. 
*/ 
if ($adapter instanceof Zend_Config) { 
if (isset($adapter->params)) { 
$config = $adapter->params->toArray(); 
} 
if (isset($adapter->adapter)) { 
$adapter = (string) $adapter->adapter; 
} else { 
$adapter = null; 
} 
} 
/* 
* Verify that adapter parameters are in an array. 
*/ 
if (! is_array($config)) { 
/** 
* @see Zend_Db_Exception 
*/ 
require_once 'Zend/Db/Exception.php'; 
throw new Zend_Db_Exception( 
'Adapter parameters must be in an array or a Zend_Config object'); 
} 
/* 
* Verify that an adapter name has been specified. 
*/ 
if (! is_string($adapter) || empty($adapter)) { 
/** 
* @see Zend_Db_Exception 
*/ 
require_once 'Zend/Db/Exception.php'; 
throw new Zend_Db_Exception( 
'Adapter name must be specified in a string'); 
} 
/* 
* Form full adapter class name 
*/ 
$adapterNamespace = 'Zend_Db_Adapter'; 
if (isset($config['adapterNamespace'])) { 
if ($config['adapterNamespace'] != '') { 
$adapterNamespace = $config['adapterNamespace']; 
} 
unset($config['adapterNamespace']); 
} 
// Adapter no longer normalized- see http://framework.zend.com/issues/browse/ZF-5606 
$adapterName = $adapterNamespace . '_'; 
$adapterName .= str_replace(' ', '_', 
ucwords(str_replace('_', ' ', strtolower($adapter)))); 
/* 
* Load the adapter class. This throws an exception 
* if the specified class cannot be loaded. 
*/ 
if (! class_exists($adapterName)) { 
require_once 'Zend/Loader.php'; 
Zend_Loader::loadClass($adapterName); 
} 
/* 
* Create an instance of the adapter class. 
* Pass the config to the adapter class constructor. 
*/ 
$dbAdapter = new $adapterName($config); 
/* 
* Verify that the object created is a descendent of the abstract adapter type. 
*/ 
if (! $dbAdapter instanceof Zend_Db_Adapter_Abstract) { 
/** 
* @see Zend_Db_Exception 
*/ 
require_once 'Zend/Db/Exception.php'; 
throw new Zend_Db_Exception( 
"Adapter class '$adapterName' does not extend Zend_Db_Adapter_Abstract"); 
} 
return $dbAdapter; 
} 
}

最上方的注释非常值得看,它清楚的说明了这个工厂,另外一段比较重要的几段代码(忽略其中的异常处理)是:
//factory有一个参数叫做$adapter 
public static function factory($adapter, $config = array()) //确定namespace 
$adapterNamespace = 'Zend_Db_Adapter'; 
//用namespace和上面传入的$adapter构造类名 
$adapterName = $adapterNamespace . '_'; 
$adapterName .= str_replace(' ', '_', ucwords(str_replace('_', ' ', strtolower($adapter)))); 
//用上面生成的类名new出obj,看起来PHP比java方便那么一点点哈(Class.forName(‘XXX').newInstance()) 
$dbAdapter = new $adapterName($config);

在回想上面使用Zend_Db::factory生成$db的地方:
$db = Zend_Db::factory('Pdo_Mysql', array( 
'host' => '127.0.0.1', 
'username' => 'webuser', 
'password' => 'xxxxxxxx', 
'dbname' => 'test' 
));

factory方法的第一个参数即是$adapter为Pdo_Mysql,记住这里是Pdo_Mysql,再跳跃一下,根据上面的$adapterNamespace = ‘Zend_Db_Adapter';可以看到生成的找到$dbAdapter的值最终必为:Zend_Db_Adapter_Pdo_Mysql,ok,根据此名字找到zend/db/adapter/pdo目录下,哈,这么多熟悉的面孔,看到了熟悉的MySql、Mssql、Sqlite这些老面孔了。
Zend Framework中的简单工厂模式 图文

注意,注意,里面还有个低调的Abstract.php,里面他们的父类Zend_Db_Adapter_Pdo_Abstract。打开Mysql.php可以看到
class Zend_Db_Adapter_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Abstract

嗯,类名Zend_Db_Adapter_Pdo_Mysql和上面生成的名字一样滴,在看看其他几个文件里面的类,他们都继承自Zend_Db_Adapter_Pdo_Abstract,如果要画类图,那就应该会有如下这么一张类图:
Zend Framework中的简单工厂模式 图文
接着再加入调用着Client和工厂函数所在的位置Zend_Db,这张简单的类图就应该是,
Zend Framework中的简单工厂模式 图文
一个非常非常纯净的简单工厂就这么出来了(不像简单工厂类图吗?那只是因为类的位置没放好)。

PHP 相关文章推荐
用php+mysql一个名片库程序
Oct 09 PHP
php 高性能书写
Dec 11 PHP
php数组的概述及分类与声明代码演示
Feb 26 PHP
php检测useragent版本示例
Mar 24 PHP
PHP操作文件的一些基本函数使用示例
Nov 18 PHP
php生成随机颜色方法汇总
Dec 03 PHP
PHP提示Warning:phpinfo() has been disabled函数禁用的解决方法
Dec 17 PHP
thinkphp在低版本Nginx 下支持PATHINFO的方法分享
May 27 PHP
php基于ob_start(ob_gzhandler)实现网页压缩功能的方法
Feb 18 PHP
基于Codeigniter框架实现的student信息系统站点动态发布功能详解
Mar 23 PHP
yii2中关于加密解密的那些事儿
Jun 12 PHP
Laravel 集成微信用户登录和绑定的实现
Dec 27 PHP
工厂模式在Zend Framework中应用介绍
Jul 10 #PHP
PHP中防止直接访问或查看或下载config.php文件的方法
Jul 07 #PHP
php数据库配置文件一般做法分享
Jul 07 #PHP
php中配置文件操作 如config.php文件的读取修改等操作
Jul 07 #PHP
php中CI操作多个数据库的代码
Jul 05 #PHP
PHP5权威编程阅读学习笔记 附电子书下载
Jul 05 #PHP
PHP中使用unset销毁变量并内存释放问题
Jul 05 #PHP
You might like
PHP学习散记_编码(json_encode 中文不显示)
2011/11/10 PHP
PHP文件上传、客户端和服务器端加限制、抓取错误信息、完整步骤解析
2017/01/12 PHP
PHP PDO和消息队列的个人理解与应用实例分析
2019/11/25 PHP
提高javascript效率 一次判断,而不要次次判断
2012/03/30 Javascript
网页打开自动最大化的js代码
2012/08/22 Javascript
jquery对dom的操作常用方法整理
2013/06/25 Javascript
一个简单的jquery进度条示例
2014/04/28 Javascript
js实现改进的仿蓝色论坛导航菜单效果代码
2015/09/06 Javascript
跟我学习javascript的最新标准ES6
2015/11/20 Javascript
javascript表单事件处理方法详解
2016/05/15 Javascript
简单的渐变轮播插件
2017/01/12 Javascript
浅谈React Native Flexbox布局(小结)
2018/01/08 Javascript
剖析Angular Component的源码示例
2018/03/23 Javascript
jQuery实现获取form表单内容及绑定数据到form表单操作分析
2018/07/03 jQuery
使用vue 国际化i18n 实现多实现语言切换功能
2018/10/11 Javascript
详解Vue的七种传值方式
2021/02/08 Vue.js
[45:25]完美世界DOTA2联赛循环赛 PXG vs IO 第一场 11.06
2020/11/09 DOTA
python http接口自动化脚本详解
2018/01/02 Python
django使用xlwt导出excel文件实例代码
2018/02/06 Python
详解Django rest_framework实现RESTful API
2018/05/24 Python
Numpy之文件存取的示例代码
2018/08/03 Python
python 获取键盘输入,同时有超时的功能示例
2018/11/13 Python
解决python3 Pycharm上连接数据库时报错的问题
2018/12/03 Python
Python获取航线信息并且制作成图的讲解
2019/01/03 Python
Pycharm 2019 破解激活方法图文详解
2019/10/11 Python
如何更改 pandas dataframe 中两列的位置
2019/12/27 Python
python 安装教程之Pycharm安装及配置字体主题,换行,自动更新
2020/03/13 Python
selenium学习教程之定位以及切换frame(iframe)
2021/01/04 Python
Waterford加拿大官方网站:世界著名的水晶杯品牌
2016/11/01 全球购物
毕业生的自我鉴定
2013/10/29 职场文书
工厂门卫岗位职责
2013/11/25 职场文书
十佳护士获奖感言
2014/02/18 职场文书
2014全国两会学习心得体会1000字
2014/03/10 职场文书
协议书范本
2014/04/23 职场文书
先进党支部申报材料
2014/12/24 职场文书
高考要来啦!用Python爬取历年高考数据并分析
2021/06/03 Python