PHP设计模式----单例模式(singleton)

提出问题: 
为什么使用单例模式?

 
  1. 对于系统中的某些类来说,只有一个实例很重要,例如,1、一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;2、在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态,因此任务窗口必须唯一;3、我们在链接数据库的时候,在整个系统中,即使我们已经封装好一个对数据库的链接的类,但是如果有好多地方都要进行数据库的链接和操作,分别实例化这个类,那么将会产生大量的数据操作,每次都要new操作,但是每次new都会消耗大量的内存资源和系统资源,而且每次打开和关闭数据库连接都 是对数据库的一种极大考验和浪费。

  2.  
  3. 因此,单例模式将是一个很好的解决方法。

单例模式有以下3个特点:

1.某个确定的类只能有一个实例。

2.必须自行创建这个实例。

3.必须给其他对象(整个系统)提供这一实例。

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

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

如下代码:

 
  1. <?php

  2. class MysqlConn

  3. {

  4. // MYSQL数据库连接信息

  5. const MYSQLHOSTNAME = "127.0.0.1";

  6. const MYSQLUSERNAME = "root";

  7. const MYSQLPASSWORD = "123456";

  8. const MYSQLDBNAME = "test";

  9. const MYSQLCHARSET = "utf8";

  10.  
  11. /**

  12. * Description:mysql数据库连接函数

  13. * Return value:连接成功返回数据库连接句柄;连接失败返回错误消息

  14. */

  15. public function MysqlConnect()

  16. {

  17. $db = new mysqli(self::MYSQLHOSTNAME, self::MYSQLUSERNAEM, self::MYSQLPASSWORD, self::MYSQLDBNAME); // 连接数据库

  18. $db->set_charset(self::MYSQLCHARSET);

  19. if (mysqli_connect_errno())

  20. {

  21. throw new CircleMysqlException("服务器系统故障", 1001);

  22. }

  23. else

  24. {

  25. return $db;

  26. }

  27. }

  28. }

  29. ?>

缺陷: 
每次数据库连接都要new这个类,然后调用mysqlconnect方法,返回close掉,频繁的new和数据库连接关闭操作是非常消耗资源的

改进: 
每次应该直接返回当前应用中已经打开的数据库连接句柄,如果关闭了,再重新打开。

这里使用单例模式如下:

 
  1. <?php

  2. class Singleton

  3. {

  4. /**

  5. * Description:(1)静态变量,保存全局实例,跟类绑定,跟对象无关

  6. * (2)私有属性,为了避免类外直接调用 类名::$instance,防止为空

  7. */

  8. private static $instance;

  9.  
  10. /**

  11. * Description:数据库连接句柄

  12. */

  13. private $db; //可由前面的类获取得到或者整合进来

  14.  
  15. /**

  16. * Description:私有化构造函数,防止外界实例化对象(如果外界能实例化,那么也会产生大量的数据库操作)

  17. */

  18. private function __construct()

  19. {

  20. }

  21.  
  22. /**

  23. * Description:私有化克隆函数,防止外界克隆对象

  24. */

  25. private function __clone()

  26. {

  27. }

  28.  
  29. /**

  30. * Description:静态方法,单例访问统一入口

  31. * @return Singleton:返回应用中的唯一对象实例

  32. */

  33. public static function GetInstance()

  34. {

  35. if (!(self::$instance instanceof self))

  36. {

  37. self::$instance = new self();

  38. }

  39. return self::$instance;

  40. }

  41.  
  42. /**

  43. * Description:获取数据库的私有方法的连接句柄

  44. */

  45. public function GetDbConnect()

  46. {

  47. return $this->db;

  48. }

  49. }

  • 需要一个保存类的唯一实例的静态成员变量(通常$instance为私有变量)
  • 构造函数和克隆函数必须声明为私有的,为了防止外部程序new类从而失去单例模式意义
  • 必须提供一个访问这个实例的公共静态方法,从而返回唯一实例的一个引用

以上内容参考自http://www.cnblogs.com/lh460795/archive/2013/07/30/3225650.html

再举一个例子: 
假设现在我们需要一个类用来保存应用程序的程序信息,比如作者,电话号码,地址等,这些信息在你每次部署程序时都会有所不同。该对象也可被用作一个“公告板”,它是系统中的其他无关对象设置和获取消息的中心。比如A这个对象负责设置作者,B这个对象负责设置地址,C这个对象可能要得到该应用程序的作者和电话号码。
那么问题来了,假如这个类,明确是这个对象,如果对于A、B、C、不是唯一的,即它们操作的对象不一样,那就会出现作者不唯一,电话号码不唯一,C获取到的信息是空白的。因此这里我们就要用到单例模式了:

 
  1. <?php

  2. class Singleton{

  3. private $props = array();//用来保存作者,电话号码,地址等

  4.  
  5. private static $instance;//该类的实例,私有防止被外界使用

  6.  
  7. private function __construct(){ }//构造函数私有,防止在外部被实例化

  8.  
  9. private function __clone(){ }//防止被克隆产生相同的对象

  10.  
  11. public static function getInstance(){

  12.  
  13. if(!self::$instance instanceof self){

  14.  
  15. self::$instance = new self();

  16.  
  17. }

  18.  
  19. return self::$instance;

  20. }

  21.  
  22. //设置对象属性

  23. public function setProperty($key,$val){

  24.  
  25. $this->props[$key] = $val;

  26.  
  27. }

  28.  
  29. //获取对象属性

  30. public function getProperty($key){

  31.  
  32. return $this->props[$key];

  33.  
  34. }

  35. }

好了,现在我们使用一下这个类是否满足我们的需求

 
  1. $pref = Singleton::getInstance();

  2.  
  3. $pref->setProperty('name','LSGO实验室');

  4.  
  5. unset($pref);//销毁引用

  6.  
  7. $pref2 = Singleton::getInstance();

  8.  
  9. echo $pref2 = getProperty('name');

  10.  
  11. //最终返回LSGO实验室,而不是空白,证明$pref和$pref2操作的是同一个对象

单例模式就介绍完了,其实一开始我想,假如我用一个全局变量来保存这个特定的对象,然后在任意的一个无关对象里面也能实现这些功能啊,但是,全局变量是不安全的,你能保证在其他地方不会存在同名的全局变量把该变量覆盖了??在面向对象的开发环境中,单例模式是一种对于全局变量的改进。

猜你喜欢

转载自blog.csdn.net/zhao_teng/article/details/87689798