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 相关文章推荐
用Socket发送电子邮件
Oct 09 PHP
通过html表格发电子邮件
Oct 09 PHP
PHP5 的对象赋值机制介绍
Aug 02 PHP
Zend的MVC机制使用分析(二)
May 02 PHP
判断php数组是否为索引数组的实现方法
Jun 13 PHP
Codeigniter注册登录代码示例
Jun 12 PHP
smarty内置函数{loteral}、{ldelim}和{rdelim}用法实例
Jan 22 PHP
PHP实现加密文本文件并限制特定页面的存取的效果
Oct 21 PHP
php实现购物车产品删除功能(2)
Jul 23 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
Jul 20 PHP
TP5框架实现的数据库备份功能示例
Apr 05 PHP
php实现微信和支付宝支付的示例代码
Aug 11 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字符串word末字符实现大小写互换的方法
2014/11/10 PHP
php实现网站顶踩功能的完整前端代码
2015/07/19 PHP
html a标签-超链接中confirm方法使用介绍
2013/01/04 Javascript
关于query Javascript CSS Selector engine
2013/04/12 Javascript
Document:getElementsByName()使用方法及示例
2013/10/28 Javascript
JavaScript对内存分配及管理机制详细解析
2013/11/11 Javascript
利用JavaScript检测CPU使用率自己写的
2014/03/22 Javascript
js获取字符串最后一位方法汇总
2014/11/13 Javascript
jQuery搜索子元素的方法
2015/02/10 Javascript
jQuery实现鼠标悬停显示提示信息窗口的方法
2015/04/30 Javascript
小白谈谈对JS原型链的理解
2016/05/03 Javascript
详解JavaScript中基于原型prototype的继承特性
2016/05/05 Javascript
微信小程序 限制1M的瘦身技巧与方法详解
2017/01/06 Javascript
javascript 面向对象实战思想分享
2017/09/07 Javascript
微信小程序表单弹窗实例
2018/07/19 Javascript
create-react-app安装出错问题解决方法
2018/09/04 Javascript
Vue+elementui 实现复杂表头和动态增加列的二维表格功能
2019/09/23 Javascript
jquery轮播图插件使用方法详解
2020/07/31 jQuery
python基础教程之lambda表达式使用方法
2014/02/12 Python
python基础教程之常用运算符
2014/08/29 Python
Python中内置数据类型list,tuple,dict,set的区别和用法
2015/12/14 Python
Python读取Word(.docx)正文信息的方法
2018/03/15 Python
详解Django中views数据查询使用locals()函数进行优化
2020/08/24 Python
matplotlib之pyplot模块坐标轴标签设置使用(xlabel()、ylabel())
2021/02/22 Python
html5标记文字_动力节点Java学院整理
2017/07/11 HTML / CSS
html5调用摄像头功能的实现代码
2018/05/07 HTML / CSS
Canvas多边形绘制的实现方法
2019/08/05 HTML / CSS
经济实惠的名牌太阳镜和眼镜:Privé Revaux
2021/02/07 全球购物
Perfume’s Club澳大利亚官网:西班牙领先的在线美容店
2021/02/01 全球购物
网络教育自我鉴定
2014/02/04 职场文书
火车来了教学反思
2014/02/11 职场文书
《第一朵杏花》教学反思
2014/04/16 职场文书
《欢乐的泼水节》教学反思
2014/04/22 职场文书
浅谈node.js中间件有哪些类型
2021/04/29 Javascript
Nginx实现会话保持的两种方式
2022/03/18 Servers
Python+Pillow+Pytesseract实现验证码识别
2022/05/11 Python