简介
单件模式是设计模式里面最常见的一种,也是设计模式初学者最先碰到的一种。在php的设计模式中,单件模式往往被用来驾驽最复杂最危险的那一部分:全局变量。
开发新手刚开始常会遇到的一个模式,单件模式,解决一个杂乱,危险而让人恐惧的问题即全局变量. 几乎在每个应用程序的每个地方都有需要被访问的组件。 配置变量就是其最突出的例子。 开发新手常会把一个调试变量申明为全局,想在那里访问就在那里访问。 随着项目的开发你会发现这样会使应用程序得维护十分艰难。
全局变量有什么问题呢? 在这个问题上我不准备花太多时间。 这里是一段Global Variables上面 wikipedia页面片段选摘:
它们常被看作是败笔,就是因为它们的非局域性:全局变量在任何地方都有修改的可能性,(除非它所存贮的位置受保护 )而且不可与程序的其它部分相分离。一个全局变量因而很有可能产生相互依存性,而增大相互依存性就会使问题变得复杂。
问题
首先阅读这系列文章中的 介绍部分, 其中讨论了模式背后的总体概念。
继续,假设应用程序有一个 Config类,这个类存储了 应用程序的偏好相关值. 当然这些值需可以从应用程序任何类中访问。一遍一遍初始化Config类代价太大而且也没有必要。 此外,如果你需要从A类中创建一个新的在B类中引用的动态 config 变量会怎么样呢? 由于 Config 的所有实例都彼此相分离,所以这种想法是无法实现的。
请注意: 我会在例子中用到php5 代码。
view plain copy to clipboard print http://mikebernat.com/blog/Design_Patterns_-_The_Singleton
- <?php
- class Config {
- public $debug = 0;
- }
- class A {
- public function doSomething() {
- $config = new Config();
- $config->debug = 1;
- }
- }
- class B {
- public function doSomethingElse() {
- $config = new Config();
- echo $config->debug;
- }
- }
- $a = new A;
- $b = new B;
- $a->doSomething();
- $b->doSomethingElse();
- // Output: 0
- ?>
<?php class Config { public $debug = 0; } class A { public function doSomething() { $config = new Config(); $config->debug = 1; } } class B { public function doSomethingElse() { $config = new Config(); echo $config->debug; } } $a = new A; $b = new B; $a->doSomething(); $b->doSomethingElse(); // Output: 0 ?>
如你所看到的那样, Config的每个例子都彼此相分离。 改变类中一个例子的成员属性不会改变其它例子成员属性。 如何这样解决这个问题:保留这看起来有用的全局变量同时符合面向对象的程序设计 的严格标准?
解决方法
进入: The Singleton。
单件模式可以使类的例子保持不变,不管它被“初始化”了多少次。 我把初始化用引号引起来,因为不是真的初始化了。它像全局变量那样作用。 如果你改变了一个类中的一个属性,就会在所有其它地方都表现出来。 看看在下面例子中如何使用单件模式修改 Config类。
view plain copy to clipboard print http://mikebernat.com/blog/Design_Patterns_-_The_Singleton
- <?php
- class Config {
- private static $instance;
- private function __construct() { }
- public static function getInstance() {
- if (emptyempty(self::$instance)) {
- self::$instance = new Config;
- }
- return self::$instance
- }
- }
- ?>
<?php class Config { private static $instance; private function __construct() { } public static function getInstance() { if (empty(self::$instance)) { self::$instance = new Config; } return self::$instance } } ?>
你首先要注意是静态属性 $instance。 这样就会在运行的时候保持类中的唯一实例。 构造函数已经设为了非公开,这就在以常用方式初始化这个类会给出错误结果。相反,我们驱使这个类的用户访问静态方法getInstance()。 getInstance() 就是单件模式神奇之处。 如果函数是第一次调用,就会产生一个新实例,存放在$instance。 而后,返回此类中这个实例。
那这是什么意思呢? 我们回头来看原来的例子。
view plain copy to clipboard print http://mikebernat.com/blog/Design_Patterns_-_The_Singleton
- <?php
- class Config {
- public $debug = 0;
- private static $instance;
- private function __construct() { }
- public static function getInstance() {
- if (emptyempty(self::$instance)) {
- self::$instance = new Config;
- }
- return self::$instance
- }
- }
- class A {
- public function doSomething() {
- $config = Config::getInstance();
- $config->debug = 1;
- }
- }
- class B {
- public function doSomethingElse() {
- $config = Config::getInstance();
- echo $config->debug;
- }
- }
- $a = new A;
- $b = new B;
- $a->doSomething();
- $b->doSomethingElse();
- // Output: 1
- ?>
<?php class Config { public $debug = 0; private static $instance; private function __construct() { } public static function getInstance() { if (empty(self::$instance)) { self::$instance = new Config; } return self::$instance } } class A { public function doSomething() { $config = Config::getInstance(); $config->debug = 1; } } class B { public function doSomethingElse() { $config = Config::getInstance(); echo $config->debug; } } $a = new A; $b = new B; $a->doSomething(); $b->doSomethingElse(); // Output: 1 ?>
如你所见,开始的 Config函数已由getInstance()静态调用替换. 这次,改变类A属性$debug时,这一操作也会在其它类中实现。
结果
使用单件模式节省大量服务器内存和CPU周期,因为它不需要一次一次地初始化(这取决于你使用那种语言)。保存 变量 应用程序.
不理想的是,单件模式也还是有全局变量所存在的问题。 产生了依存关系但很难发现依存关系。这有损于模式化。 不过,如果保守小心使用单件模式,我想对你还是大有帮助的.
其他意见
单件. 不单是因其非常有用而有其它原因。 不是因为合理使用时怎样的有效,开发者. 不管怎样,先看看这里的几个例子:
编辑: 如果你遵守模式定义,在合适的状况下使用它,我想这些言论就有点吹毛求疵了: