Aus*_*yde 13 php singleton design-patterns
所以,我总是像这样实现一个单例:
class Singleton {
private static $_instance = null;
public static function getInstance() {
if (self::$_instance === null) self::$_instance = new Singleton();
return self::$_instance;
}
private function __construct() { }
}
Run Code Online (Sandbox Code Playgroud)
但是,它最近让我感到震惊,我也可以使用成员方式的静态变量来实现它:
class Singleton {
public static function getInstance() {
//oops - can't assign expression here!
static $instance = null; // = new Singleton();
if ($instance === null) $instance = new Singleton();
return $instance;
}
private function __construct() { }
}
Run Code Online (Sandbox Code Playgroud)
对我来说,这是更清洁,因为它不会使类混乱,我不需要做任何明确的存在检查,但因为我从来没有在其他任何地方见过这个实现,我想知道:
使用第二个实现比第一个实现有什么问题吗?
irc*_*ell 10
去一个类属性.有一些优点......
class Foo {
protected static $instance = null;
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new Foo();
}
return self::$instance;
}
}
Run Code Online (Sandbox Code Playgroud)
首先,执行自动化测试更容易.你可以创建一个模拟Foo类为"替换"的情况下,使依赖于foo其他类将得到模拟,而不是原件及复印件:
class MockFoo extends Foo {
public static function initialize() {
self::$instance = new MockFoo();
}
public static function deinitialize() {
self::$instance = null;
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在您的测试用例中(假设phpunit):
protected function setUp() {
MockFoo::initialize();
}
protected function tearDown() {
MockFoo::deinitialize();
}
Run Code Online (Sandbox Code Playgroud)
这解决了他们难以测试的单身人士的共同抱怨.
其次,它使您的代码更加灵活.如果您想在该类中运行时"替换"该功能,您需要做的就是将其子类化并替换self::$instance.
第三,它允许您在其他静态函数中操作实例.对于单实例类(真正的单例)来说,这不是一个大问题,因为你可以调用它self::instance().但是,如果您有多个"命名"副本(比如数据库连接或其他需要多个资源的资源,但如果它们已经存在则不想创建新副本),它会变脏,因为您需要跟踪的名称:
protected static $instances = array();
public static function instance($name) {
if (!isset(self::$instances[$name])) {
self::$instances[$name] = new Foo($name);
}
return self::$instances[$name];
}
public static function operateOnInstances() {
foreach (self::$instances as $name => $instance) {
//Do Something Here
}
}
Run Code Online (Sandbox Code Playgroud)
另外请注意,我不会将构造函数设为私有.这将使得无法正确扩展或测试.相反,让它受到保护,以便您可以根据需要进行子类化并仍然对父级进行操作...
你可能意味着稍作修改(否则会出现语法错误):
<?php
class Singleton {
public static function getInstance() {
static $instance;
if ($instance === null)
$instance = new Singleton();
xdebug_debug_zval('instance');
return $instance;
}
private function __construct() { }
}
$a = Singleton::getInstance();
xdebug_debug_zval('a');
$b = Singleton::getInstance();
xdebug_debug_zval('b');
Run Code Online (Sandbox Code Playgroud)
这给出了:
实例:(refcount = 2,is_ref = 1), object(Singleton)[ 1 ]
a :( refcount = 1,is_ref = 0), object(Singleton)[ 1 ]
实例:(refcount = 2,is_ref = 1), object(Singleton)[ 1 ]
b:(refcount = 1,is_ref = 0), object(Singleton)[ 1 ]
因此它的缺点是每次调用都会创建一个新的zval.这不是特别严重,所以如果您愿意,请继续.
强制zval分离的原因是inside getInstance,$instance是一个引用(在某种意义上=&,它有引用计数2(一个用于方法内部的符号,另一个用于静态存储).由于getInstance不通过引用返回, zval必须分开 - 对于返回,将创建一个新的参考计数1并且参考标志清除.