提出问题:
为什么使用单例模式?
对于系统中的某些类来说,只有一个实例很重要,例如,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面向对象、模式与实践》一书和LSGO团队老大给我的讲解