PHP设计模式介绍Ⅳ单件模式

   几乎所有面向对象的程序中,总有一两个资源被创建出来,在程序应用中持续被共享使用。例如,这样的一个资源,在一个电子商务程序的数据库连接中使用:这个连接在应用程序启动时初始化,程序于是可以有效的执行;当程序结束时,这个连接最终被断开并销毁。如果是你写的代码,没必要在每时每刻创建一个数据库连接,这样非常低效。已经建立好的连接应该能被你的代码简单重复的使用。这个问题就是,基于以上要求你将如何进行这个数据库连接?(或者连接其它被循环使用的唯一资源,比如一个开放文件或者一个队列。)

  问题

  你怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且它很存取容易呢?

  解决方案

  当然,全局变量是显而易见的解决方案。但它就像潘多拉的盒子(正确的判断来自经验,而错误的判断产生经验。这句谚语就是这个意思。),你的任何代码都能修改全局变量,这将不可避免的引起更多调试的意外。换句话说,全局变量的状态总是会出现一些问题的,(这里有一个关于全局变量使用问题不错的描述,http://c2.com/cgi/wiki?GlobalVariablesAreBad)。

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

  关键问题是,如何在每时每刻获得一个精确统一的实例。请看下面的例子:

  // PHP4

  function TestGetInstance() {

  $this->assertIsA(

  $obj1 =& DbConn::getInstance(),

  ‘DbConn’,

  ‘The returned object is an instance of DbConn’);

  $this->assertReference(

  $obj1,

  $obj2 =& DbConn::getInstance(),

  ‘Two calls to getInstance() return the same object’);

  }

  注释:assertReference

  assertReference() 方法确保两个被传递的参数引用自相同的PHP变量。

  在PHP4中,这里断言两个被测试的参数的却是相同的对象。assertReference() 这个方法在移植到PHP5以后也许就不推荐使用了。

  这个test方法有两个断言:第一个判断第调用静态方法DbConn::getInstance()返回的值是DbConn对象的实例,第二个用来判断第二次调用getInstance()方法返回得值引用的是相同的对象实例,这意味着他们使用的是同一个对象。

  除了断言代码预期的执行结果,Test也预示了getInstance()的正确用法(PHP4):$local_conn_var=&DbConn::getInstance()。引用(=&)静态方法的返回值赋值给了这个局部变量。

  再写另外一段测试代码:直接用“new”来实例化一个单件类会引起某些类型的错误。test代码如下:

  function TestBadInstantiate() {

  $obj =& new DbConn;

  $this->assertErrorPattern(

  ‘/(bad|nasty|evil|do not|don’t|warn).*’.

  ‘(instance|create|new|direct)/i’);

  }

  这段代码直接创建了一个 DbConn 的实例,将会引起PHP报错。为了让代码更稳定,我们用PCRE正则表达式来匹配报错信息。(显示报错信息的确切措词并不重要。)

  [next]

  样本代码

  单件模式是一个很有趣的模式。让我们用PHP4和PHP5两种方式来探究它的实现过程,现在从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];

  }

  }

  在DbConn的构造函数中,你可能对$fromGetInstance的默认参数感到疑惑。在对象被直接实例化时,它能够提供(很微弱的)保护:除非这个默认值变成e (在PHP的数学常量中 M_E = 2.718281828459),否则这段代码会报错。

  表示成一个UML类图,解决办法如下:


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

  下面有个例子,它解释了为什么构造函数保护代码有一个全局的标识:

  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]);

  }

  提示

  PHP4允许你改变构造函数中$this的值。在过去,我们会习惯设置 $this = null;当有一个创建构造错误时,确保无效的对象不能被代码继续使用。PHP4中很有用的东西,在PHP5中并不兼容,将来会在你的代码中得到验证,这种技术不再被推荐。

  这段代码中另外一个重点是引用操作&的用法。有两种地方需要使用&。第一种是在函数定义时,在函数名字前用来表示将返回一个引用。第二种是将新的DbConn对象赋值给$GLOBALS数组。(在序言和值对象章节中提到过:在PHP4中,你总会使用 &操作符,以引用的方式创建、传递和返回对象,)

  getInstance()方法的条件检查,常常被写成没有警示的情况下运行,甚至在E_ALL的错误级别下也不会提示。它检查在$GLOBAL数组中适当的位置是否有一个DbConn对象,如果没有,就在那里创建这个对象。这个方法于是返回了这样的结果,这个对象能被重复创建或者这个对象在之前已经被这个方法创建过了。当方法结束时,你可以确认已经拥有这个类的有效实例,而且它已经被有效初始化。

  [next]

  静态方式

  关于全局变量的问题,甚至隐藏在getInstance()中的全局变量中也存在。因为全局变量在脚本的任何地方都有效,在没有注意到的情况下,你依然有可能破坏这个全局变量,

  在getInstance()方法内部使用静态变量来存储Singleton是一个显得干净的办法。第一个代码片断如下:

  class DbConn {

  // ...

  function &getInstance() {

  static $instance = false;

  if (!$instance) $instance =& new DbConn(M_E);

  return $instance;

  }

  }

  Zend 1引擎在PHP4中不能存储静态变量的引用 (请看http://www.php.net/manual/en/language.variables.scope.php#AEN3609)。使用一个工作区存储静态数组,并且将这个单件实例的引用放置到一个已知的数组中。getInstance()方法如下:

  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() {

  static $instance = array();

  if (!$instance) $instance0 =& new DbConn(M_E);

  return $instance0;

  }

  }

  这段代码很简单的选择了这个静态数组$instancede的第一个元素,用来保持单件DbConns实例的引用。

  虽然这段代码有点依赖PHP的布尔方式,但它比那个全局版本更严谨:在条件检测时,使用一个空的数组会得到结果false。就像在DbConn类的前一个版本一样,在函数的定义和赋值部分需要引用操作符。

  PHP5中的单件模式

  PHP5中更容易实现单件模式,PHP5对于类内部变量和函数的访问控制被加强了。将DbConn::_construct()构造方法设置为私有(private),这个类就不能被直接实例化。用UML图表示,PHP5的DbConn单件模式如下:


  组合使用静态方法和静态变量保持这个实例,并且设置构造函数为私有,以防止直接实例化类而创建实例,代码如下:

  class DbConn {

  /**

  * static property to hold singleton instance

  */

  static $instance = false;

  /**

  * constructor

  * private so only getInstance() method can instantiate

  * @return void

  */

  private function __construct() {}

  /**

  * factory method to return the singleton instance

  * @return DbConn

  */

  public function getInstance() {

  if (!DbConn::$instance) {

  DbConn::$instance = new DbConn;

  }

  return DbConn::$instance;

  }

  }

时间: 2024-10-29 08:06:58

PHP设计模式介绍Ⅳ单件模式的相关文章

JavaScript设计模式之单件模式介绍_javascript技巧

单件模式说明 1. 说明:单件模式,就是静态化的访问中已经实例化的对象,这个对象只能通过一个唯一的入口访问,已经实例或待实例化的对象:面向对象语言如Java, .Net C#这样的服务端动态语言里,能保证类的操作有顺的进行,避免并行操作使数据发生混乱的情况: 2. 单件模式的好处: 1>. 减少 new 的操作,以免加快内存频繁操作,占用内存: 2>. 尽量减少大系统的对象的开销: 3>. 就是上面说的,可以保障某些类型的操作有顺序准确的操作,以免并行处理引起的数据异常现象: 当然上面说

.NET设计模式:单件模式(Singleton Pattern)

概述 Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点.这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任. 从另一个角度来说,Singleton模式其实也是一种职责型模式.因为我们创建了一个对象,这个对象扮演了独一无二的角色,在这个单独的对象实例中,它集中了它所属类的所有权力,同时它也肩负了行使这种权力的职责! 意图

艾伟_转载:.NET设计模式:单件模式(Singleton Pattern)

概述 Singleton模式要求一个类有且仅有一个实例,并且提供了一个全局的访问点.这就提出了一个问题:如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例?客户程序在调用某一个类时,它是不会考虑这个类是否只能有一个实例等问题的,所以,这应该是类设计者的责任,而不是类使用者的责任. 从另一个角度来说,Singleton模式其实也是一种职责型模式.因为我们创建了一个对象,这个对象扮演了独一无二的角色,在这个单独的对象实例中,它集中了它所属类的所有权力,同时它也肩负了行使这种权力的职责! 意图

php设计模式总结-单件模式

一.单件模式 英文叫做sington.其他语言中有叫做单例模式,其实都是一样的道理.保证只会出现单个实例,所以是单例.翻译成单件,永远只会产生一件,呵呵. 还有翻译成单元素模式.其实关键是看这个英文比较好.英文是sington,统一是使用这个单词. 单件模式的目的我理解如下: 避免重复创建(实例化)对象,已经有现成的实例就用现成的. 减少资源的浪费(因为创建多个实例,浪费内存,完全没必要),单件模式保证了每时每刻引用的都是同一个实例. 为什么同时创建多个实例会引起逻辑上的错误呢? $obj1$o

.NET简谈设计模式之(单件模式)

我们继续学习设计模式系列文章. 今天要讲的是单件模式,其实单件模式是比较简单的设计模式,在我们日常开发过程中也是经常用到的. 单件模式:单件模式是一种用于确保整个应用程序中只有一个类实例且这个实例所占资源在整个应用程序中是共享时的程序设计方法. 我想如果初学者对一些基本概念能熟练掌握时,其实单件模式是很简单的,就是因为初学者对一些概念还不是很理解,我们先来扫除这些阻挡我们理解的障碍. 大部分的初学者都会搞不清楚"静态对象"与"实例对象"的区别.当然谁都有一个过渡期,

设计模式之单件模式(Singleton Pattern )

设计 单件模式 Singleton Pattern Singleton 模式,它包含在创造性模式系列中. 创造性模式指示如何以及何时创建对象.Singleton 模式可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点.在程序设计过程中,有很多情况需要确保一个类只能有一个实例.例如,系统中只能有一个窗口管理器.一个打印假脱机,或者一个数据引擎的访问点.PC机中可能有几个串口,但只能有一个COM1实例. 其结构如下: 我们可以定义一个Spooler类,实现Singleton 模式 Publ

温故而知新:设计模式之单件模式(Singleton)

 1 using System; 2  3 namespace Singleton 4 { 5     class Program 6     { 7         static void Main(string[] args) 8         { 9             Singleton s1 = Singleton.Instance;10             Singleton s2 = Singleton.Instance;11 12             Console

设计模式应用实例之单件模式应用,和简单注入实现形式

上次写了基于MVC模式和Command模式构建系统架构,也做了一个简单的实例. 在把我想写的写完以后,我想通过一个具体的项目来串联大部分的设计模式思想. 今天我想说的是单件模式. 首先简单介绍下单件模式是什么东西,也称Singleton模式,实现单一实例的功能,不具体说了,不了解者看别的介绍概念性文章. 通常,在我做过的项目中,应用Singleton最多的就是做适配器,下面我会介绍如何做适配器. 先介绍通过通过单件模式,实现适配器产生单一实例的应用过程. 在介绍单件模式以前,我想先介绍下,面向服

设计模式(二)单件模式Singleton(创建型)

SINGLETON(单件)-对象创建型模式        几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的.您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销.再如大家最经常用的IM,如QQ,在同一台电脑,一个帐号只能有唯一的登录. 1. 问题 怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且这个实例易于被访问呢? 2. 解决方案 1)全局变量:一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个