Zend Framework中的简单工厂模式 图文
程序员文章站
2023-12-05 13:49:46
前段时间用来zf,把他当黑盒感觉不是很好,一直有看其源码的冲动,但是。。。如果一点一点点看的话,挑战确实有些大了。某天又然后想到好久没复习设计模式了。综合一下,复习一个设计...
前段时间用来zf,把他当黑盒感觉不是很好,一直有看其源码的冲动,但是。。。如果一点一点点看的话,挑战确实有些大了。某天又然后想到好久没复习设计模式了。综合一下,复习一个设计模式之后在zf中找一下使用这模式的源码吧,不读所有源码,读读比较”高级”的部分吧,要说模式,暂时不知道是不是所有模式zf里面都有,但是应该有足够的模式够我最近看了,在说可以找找其他开源的软件来找模式。这段时间被各种笔试神马乱七八糟的把生活搞得稍微有点乱,但是不管怎样,复习还是必须的吧。再说一下zf吧,zf一个好处就是各个component比较独立,component之间没有过多的依赖,这样一来,为使用者提供了方便,当然也为我这样无聊且懒的想看源码的人提供了方便。
今天看看简单工厂,zf里面不缺模式,更不缺工厂模式,大名鼎鼎的的 zend_db就毫不吝啬的使用简单工厂,再ctrl+h(zend studio下)一下会发现factory特别多,如果没猜错应该大多应该也是简单工厂。由于zend_db 最常用,我也就自然的会比较想看一下他的实现。在查看源码之前先复习一下怎么用zend_db和简单工厂(这里是一个stack,先复习简单工厂)。
复习简单工厂模式
用类图回忆一下,简单工厂类图:
借用《研磨设计模式》作者的一张图,可以看到client通过factory来获取对象,通过api结构来调用。用factory把具体的api的创建隐藏起来。而其他所有使用者在使用时,只需要知道用factory创建,通过api结构调用即可,简单复习完成。看到类图应该能想起简单工厂了,因为他本身确实很简单。复习完简单工厂,思维稍微跳跃一下,直接来看看zend_db的使用。
1.复习zend_db的使用
如果不知道如何使用,准备看xxx的源码却不知道怎么用xxx,这有点囧,所以先小小的看一下zend_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这些老面孔了。
注意,注意,里面还有个低调的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,如果要画类图,那就应该会有如下这么一张类图:
接着再加入调用着client和工厂函数所在的位置zend_db,这张简单的类图就应该是,
一个非常非常纯净的简单工厂就这么出来了(不像简单工厂类图吗?那只是因为类的位置没放好)。
今天看看简单工厂,zf里面不缺模式,更不缺工厂模式,大名鼎鼎的的 zend_db就毫不吝啬的使用简单工厂,再ctrl+h(zend studio下)一下会发现factory特别多,如果没猜错应该大多应该也是简单工厂。由于zend_db 最常用,我也就自然的会比较想看一下他的实现。在查看源码之前先复习一下怎么用zend_db和简单工厂(这里是一个stack,先复习简单工厂)。
复习简单工厂模式
用类图回忆一下,简单工厂类图:
借用《研磨设计模式》作者的一张图,可以看到client通过factory来获取对象,通过api结构来调用。用factory把具体的api的创建隐藏起来。而其他所有使用者在使用时,只需要知道用factory创建,通过api结构调用即可,简单复习完成。看到类图应该能想起简单工厂了,因为他本身确实很简单。复习完简单工厂,思维稍微跳跃一下,直接来看看zend_db的使用。
1.复习zend_db的使用
如果不知道如何使用,准备看xxx的源码却不知道怎么用xxx,这有点囧,所以先小小的看一下zend_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这些老面孔了。
注意,注意,里面还有个低调的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,如果要画类图,那就应该会有如下这么一张类图:
接着再加入调用着client和工厂函数所在的位置zend_db,这张简单的类图就应该是,
一个非常非常纯净的简单工厂就这么出来了(不像简单工厂类图吗?那只是因为类的位置没放好)。