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

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

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

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

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

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

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

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

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

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

如下代码:

<?php  
class MysqlConn {  
    // MYSQL数据库连接信息 
    const MYSQLHOSTNAME = "127.0.0.1";  
    const MYSQLUSERNAME = "root";  
    const MYSQLPASSWORD = "123456";  
    const MYSQLDBNAME = "test";  
    const MYSQLCHARSET = "utf8";  

    /** * Description:mysql数据库连接函数 * Return value:连接成功返回数据库连接句柄;连接失败返回错误消息 */  
    public function MysqlConnect() {  
        $db = new mysqli(self::MYSQLHOSTNAME, self::MYSQLUSERNAEM, self::MYSQLPASSWORD, self::MYSQLDBNAME); // 连接数据库 
        $db->set_charset(self::MYSQLCHARSET);  
        if (mysqli_connect_errno())  
        {  
            throw new CircleMysqlException("服务器系统故障", 1001);  
        }  
        else  
        {  
            return $db;  
        }  
    }  
}
?>  

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

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

这里使用单例模式如下:

<?php  
class Singleton  
{  
    /** * Description:(1)静态变量,保存全局实例,跟类绑定,跟对象无关 * (2)私有属性,为了避免类外直接调用 类名::$instance,防止为空 */  
    private static $instance;  

    /** * Description:数据库连接句柄 */  
    private $db;  //可由前面的类获取得到或者整合进来

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

    /** * Description:私有化克隆函数,防止外界克隆对象 */  
    private function __clone()  
    {  
    }  

    /** * Description:静态方法,单例访问统一入口 * @return Singleton:返回应用中的唯一对象实例 */  
    public static function GetInstance()  
    {  
        if (!(self::$instance instanceof self))  
        {  
            self::$instance = new self();  
        }  
        return self::$instance;  
    }  

    /** * Description:获取数据库的私有方法的连接句柄 */  
    public function GetDbConnect()  
    {  
        return $this->db;  
    }  
}  
  • 需要一个保存类的唯一实例的静态成员变量(通常$instance为私有变量)
  • 构造函数和克隆函数必须声明为私有的,为了防止外部程序new类从而失去单例模式意义
  • 必须提供一个访问这个实例的公共静态方法,从而返回唯一实例的一个引用

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

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

<?php
class Singleton{
    private $props = array();//用来保存作者,电话号码,地址等

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

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

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

    public static function getInstance(){

        if(!self::$instance instanceof self){

            self::$instance = new self();

        }

        return self::$instance;
    }

    //设置对象属性
    public function setProperty($key,$val){

        $this->props[$key] = $val;

    }

    //获取对象属性
    public function getProperty($key){

        return $this->props[$key];

    }
}

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

$pref = Singleton::getInstance();

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

unset($pref);//销毁引用

$pref2 = Singleton::getInstance();

echo $pref2 = getProperty('name');

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

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

    原文作者:php_zhaop
    原文地址: https://blog.csdn.net/php_Zhaop/article/details/73863764
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞