2010年2月23日星期二

PHP 单件模式

当需要一个特殊类的唯一实例时,使用这个名字叫单件的模式。基于单件模式的类能实例化和初始化这个类的一个实例,并且提供每时每刻绝对相同的连接。一般情况下使用名为getInstance()的静态方法实现。

PHP4 单件模式
在DbConn的构造函数中,你可能对$fromGetInstance的默认参数感到疑惑。在对象被直接实例化时,它能够提供(很微弱的)保护:除非这个默认值变成e (在PHP的数学常量中 M_E = 2.718281828459),否则这段代码会报错。
[php title="PHP4"]
class DbConn {
function DbConn($fromGetInstance=false) {
if (M_E != $fromGetInstance) {
trigger_error('The DbConn class is a Singleton,' .' please do not instantiate directly.');
}

}

function &getInstance() {
$key = '__some_unique_key_for_the_DbConn_instance__';
if (!(array_key_exists($key, $GLOBALS) && is_object($GLOBALS[$key]) && 'dbconn' == get_class($GLOBALS[$key]) )) {
$GLOBALS[$key] =& new DbConn(M_E);
}
return $GLOBALS[$key];
}

}
[/php]
如果不选用这个“神秘参数”-类型保护,建立一个全局标记是另外一个选择,用它来验证是通过getInstance()方法来创建的对象。保护方式从“知道它的名字”改变成“它存在于环境中”。

下面例子解释了为什么构造函数保护代码有一个全局的标识:
[php]
class DbConn {
function DbConn() {
$token = '__some_DbConn_instance_create_semaphore__';
if (!array_key_exists($token, $GLOBALS)) {
trigger_error('The DbConn class is a Singleton,' .' please do not instantiate directly.');
}
}

function &getInstance() {
static $instance = array();
if (!$instance) {
$token = '__some_DbConn_instance_create_semaphore__';
$GLOBALS[$token] = true;
$instance[0] =& new DbConn;
unset($GLOBALS[$token]);
}

return $instance[0];
}
}
[/php]

PHP4允许改变构造函数中$this的值。在过去,会习惯 $this = null;当有一个创建构造错误时,确保无效的对象不能被代码继续使用。但这种技术在PHP5中并不兼容。

另外,杀昂上段代码中另外一个重点是引用操作&的用法。有两种地方需要使用&。第一种是在函数定义时,在函数名字前用来表示将返回一个引用。第二种是将新的DbConn对象赋值给$GLOBALS数组。
getInstance()方法的条件检查,常常被写成没有警示的情况下运行,甚至在E_ALL的错误级别下也不会提示。它检查在$GLOBAL数组中适当的位置是否有一个DbConn对象,如果没有,就在那里创建这个对象。这个对象能被重复创建或者这个对象在之前已经被这个方法创建过。当方法结束时,可以确认已经拥有这个类的有效实例,而且它已经被有效初始化。

[php title="PHP5 单件模式"]
class DbConn {
static $instance = false;

private function __construct() {}

public function getInstance() {
if (!DbConn::$instance) {
DbConn::$instance = new DbConn;
}
return DbConn::$instance;
}
}
[/php]
组合使用静态方法和静态变量保持这个实例,并且设置构造函数为私有,例化类而创建实例,这个类就不能被直接实例化。

[php title="Monostate Pattern(单态模式):类单件模式"]
class ApplicationConfig {
var $_state;

function ApplicationConfig() {
$key = '__stealth_singleton_state_index__';
if (!(array_key_exists($key, $GLOBALS) && is_array($GLOBALS[$key]))) {
$GLOBALS[$key] = array();
}

$this->_state =& $GLOBALS[$key];
}

function set($key, $val) {
$this->_state[$key] = $val;
}

function get($key) {
if (array_key_exists($key, $this->_state)) {
return $this->_state[$key];
}
}
}
[/php]
在PHP中,通过把一个全局变量绑定到一个实例变量来实现MonoState。
这个技巧的核心是$this->state =& $GLOBALS[$key]; 。在确定$GLOBALS[$key]是一个数组后,代码绑定一个全局数组的引用给类变量$this->state。从而,任何$this->state的改变都自然而言地同步到全局数组,包括类的其它实例。

这个技巧能够在任何PHP的自动全局(superglobal)数组使用,尤其在用户消息队列$_SESSION中有很显著的效果。MonoState能通过你的代码为用户存储一系列的使用信息(要显示的信息可能是从另外一个页面传入的)。

资料:

没有评论 :

发表评论