鸡肋的PHP单例模式应用详解

单例模式的要点有三个:

  一是某个类只能有一个实例;

  二是它必须自行创建这个实例;

  三是它必须自行向整个系统提供这个实例。

  

复制代码 代码如下:

  <?php

  /* 单例模式举例,其要点如下:

  *

  * 1. $_instance 必须声明为静态的私有变量

  * 2. 构造函数和克隆函数必须声明为私有的,这是为了防止外部程序 new 类从而失去单例模式的意义

  * 3. getInstance()方法必须声明为公有的,必须调用此方法以返回唯一实例的一个引用

  * 4. ::操作符只能访问静态变量或静态函数

  * 5. PHP的单例模式是相对而言的,因为PHP的解释运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。

  * 也就是说,PHP在语言级别上没有办法让某个对象常驻内存。在PHP中,所有的变量都是页面级的,无论是全局变量,

  * 还是类的静态成员,都会在页面执行完毕后被清空,结果会重新建立新的对象,这样也就完全失去了Singleton的意义。

  * 不过,在实际应用中同一个页面中可能会存在多个业务逻辑,这时单例模式就起到了很重要的作用,有效的避免了重复

  * new 对象(注: new 对象会消耗内存资源)这么一个行为,所以我们说PHP的单例模式是相对而言的

  *

  */

  class People

  {

  static private $_instance = NULL;

  public $height = '';

  public $age = '';

  private function __construct()

  {

  $this->height = '185';

  $this->age = 25;

  }

  private function __clone()

  {

  //do something

  }

  static public function getInstance()

  {

  if(!self::$_instance instanceof self)

  {

  //echo 'lgh-big';

  self::$_instance = new self;

  }

  else

  {

  //for testing only

  //echo 'gdc-xiaoairener';

  }

  return self::$_instance;

  }

  public function getHeight()

  {

  echo $this->height;

  }

  public function getAge()

  {

  echo $this->age;

  }

  }

  function testInstance()

  {

  People::getInstance()->getAge();

  }

  //begin to use the class

  $lgh = People::getInstance();

  $lgh->getHeight();

  echo '<br />';

  testInstance();

  ?>

  优点:单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源

  缺点:在PHP中,所有的变量无论是全局变量还是类的静态成员,都是 页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只 是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。

  Why–为什么要使用PHP单例模式?

  PHP的一个主要应用场合就是应用程序与数据库打交道的应用场景,所以一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。

  还是有些抽象,给出代码片段。

  使用传统方式编码

  

复制代码 代码如下:

  <?php

  ......

  //初始化一个数据库句柄

  $db = new DB(...);

  //比如有个应用场景是添加一条用户信息:

  $db->addUserInfo();

  ......

  //然而我们在另外一个地方可能要查找用户的信息,这个情景出现在一个函数中,这时要用到数据库句柄资源,我们可能需要这么去做

  ......

  function test(){

  ......

  //这时我们不得不重新初始化一个数据库句柄,试想多个应用场景下,这样的代码是多么可怕啊?!

  $db = new DB(...);

  $db->getUserInfo();

  ......

  //有些朋友或许会说,我也可以不这样做啊,我直接利用global关键字不就可以了吗?的确,global可以解决问题,也起到了单例模式的作用,但是OOP中,我们拒绝这样来编写代码,因为global存在安全隐患,请参考相关书籍,同时单例模式恰恰是对全局变量的一种改进,避免了那些存储唯一实例的全局变量污染命名空间

  global $db;  //OOP中,我们不提倡这样编写代码

  ......

  }

  使用单例模式编码

  

复制代码 代码如下:

  <?php

  ......

  //所有的应用情景只有一个数据库句柄资源,嘿嘿,效率老高了,

  //资源也大大的得到节省,代码简洁明了:)

  DB::getInstance()->addUserInfo();

  DB::getInstance()->getUserInfo();

  ......

  How–如何来编写PHP单例模式?

  在了解了单例模式的应用场景之后,下面我们通过编写单例模式的具体实现代码来掌握PHP单例模式的核心要点,代码如下:

  

复制代码 代码如下:

  <?php

  /**

  *  PHP单例模式演示举例

  *  @author   guohua.li

  *  @modify  2010-07-11

  *  @website  http://blog.163.com/lgh_2002/

  */

  class User{

  /**

  *  静态成品变量 保存全局实例

  *  @access private

  */

  static private $_instance = NULL;

  /**

  *  私有化构造函数,防止外界实例化对象

  */

  private function  __construct() {}

  /**

  *  私有化克隆函数,防止外界克隆对象

  */

  private function  __clone(){}

  /**

  *  静态方法, 单例统一访问入口

  *  @return  object  返回对象的唯一实例

  */

  static public function getInstance() {

  if (is_null(self::$_instance) || !isset(self::$_instance)) {

  self::$_instance = new self();

  }

  return self::$_instance;

  }

  /**

  * 测试方法: 获取用户名字

  */

  public function getName() {

  echo 'hello liguohua!';

  }

  }

  从以上代码中,我们总结出PHP单例模式实现的核心要点有如下三条:

  1.需要一个保存类的唯一实例的静态成员变量(通常为$_instance私有变量)

  2.构造函数和克隆函数必须声明为私有的,这是为了防止外部程序new类从而失去单例模式的意义

  3.必须提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一实例的一个引用

  PHP单例模式的缺点

  众所周知,PHP语言是一种解释型的脚本语言,这种运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。也就是说,PHP在语言级别上没有办法让某个对象常驻内存,这和asp.net、Java等编译型是不同的,比如在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级的,真正可以做到这个实例在应用程序生命周期中的唯一性。然而在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。