单例模式
模式系列的开篇,首先介绍的是我最喜欢的单例模式(Singleton),简单而言,这属于:生成一个、且只生成一个对象实例的特殊类。
这个唯一存在的类,将替代Global关键字,并且更安全。
产生前提
全局变量是OOP程序猿遇到的主要BUG源泉之一,全局变量的冲突也是极其危险的,因为PHP不会对其产生任何级别的错误,如果结果不能直接观测,那么你的程序可能会让你感到懵圈。
尽管命名空间的出现,一定程度上的避免了全局冲突,然而,在空间内的冲突仍然可能存在。可所有的类都可以访问它,全局变量仍是巨大的诱惑。
问题
良好设计的系统:通过方法调用传递对象实例。
每个类都会与背景环境保持独立,并通过清晰的通信方式,与系统中其它部分进行无耦合协作。
但有时,你不得不通过 中间件 来沟通各个组件,中间件会导致依赖、耦合,并且,倘若组件返回的参数之一,包含了这个中间件,将会导致“依赖污染”。
怎样的中间件才能避免上述情况呢?
如同Global,它可以被所有对象使用;
不存储在全局变量中,不接受覆写;
它在整个系统中,是唯一的。
实现
为了解决这个问题,我们可以强行控制“对象实例化”,我们通过简单地定义一个私有构造方法,创建一个无法从外部实例化的类,通过静态方法与静态属性,来间接实例化它:
class Preferences {
private $props = array();
private static $instance;
private function __construct() { }
public static function getInstance() {
if (empty(self::$instance)) {
self::$instance = new Preferences();
}
return self::$instance;
}
public function setProperty($key, $val) {
$this->props[$key] = $val;
}
public function getProperty($key) {
return $this->props[$key];
}
}
单例类创建完毕后,我们进行测试:
// 设置属性
$pref = Preferences::getInstance();
$pref->setProperty("name", "UiTest");
unset($pref);
// 调用属性
$pref2 = Preferences::getInstance();
echo $pref2->getProperty("name");
最终可以得到输出:UiTest,并且,你可以去设置任何值,来测试它的可用性。
最后附赠一张UML图:
(感谢云绘图软件:ProcessOn)
总结
Bad Result:单例与全局变量都会:创建难以调试的依赖关系、被误用、绕过安全的通信接口,所以,需要谨慎小心的部署单例类。
Nice Result:与全局变量不同,单例的任何错误与冲突,都将以报错的形式出现(除非你关闭了错误提示),这下,你就可以放心的使用它了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。