正文
在OOP中,一个对象只负责一个特定的任务通常是一种很好的做法。例如,你也许希望只让一个对象去访问数据库。单例模式被认为是职责模式,这是因为它将创建对象的控制权委托到一个单一的访问点上。
在任何时候,应用程序中都会只有这个类仅有的一个实例存在。
这可以防止我们去打开数据库的多个连接,或者不必要得使用多余的系统资源。
在更加复杂的系统中,使用单例模式在维持系统程序状态的同步方面也尤其有用。
所有的单例模式至少拥有以下三个公共元素:
- 它们必须拥有一个构造函数,并且被标记为private。
- 它们拥有一个保存类的实例的静态成员变量。
- 它们拥有一个访问这个实例的公共的静态方法。
和普通类不同的是,单例模式不能在其它类中直接实例化。单例模式只能被其自身实例化。要获得这种限制效果,__construct()方法必须被标记为private。如果试图用private构造函数构造一个类,就会得到一个可访问性级别的错误。
要让单例类起作用,就必须使其为其他类提供一个实例,用它调用各种方法。单例类不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。结果是单例类不会重复占用内存和系统资源,从而让应用程序的其他部分更好的使用这些资源。作为这一模式的一部分,必须创建一个空的私有的__clone()方法,以防止对象呗复制或者克隆。
返回实例引用的这个方法通常被命名为getInstance()。这个方法必须是静态的,而且如果它还没有实例化,就必须实例化。getInstance()方法通过使用instanceof操作符和self关键字,可以检测到类是否已经被初始化。如果保存实例的静态成员为空或者还不是类自身的一个实例,那么这个实例将会被创建并保存到存放实例的变量中。
示例
<?php
class UniqueRandNumber
{
private $_num;
static $_instance;
////$urn = new UniqueRandNumber();非法,因为构造函数是私有的
private function __construct()
{
echo "construct\n";
$this->_num = rand(1, 100); //生成[1,100]之间的一个随机整数
}
//将__clone()设置为私有,则$urn2 = clone $urn1;这句话不合法
private function __clone()
{
}
public static function getInstance()
{
if(!(self::$_instance instanceof self)){
self::$_instance = new self();
}
return self::$_instance;
}
public function fetch()
{
return $this->_num;
}
}
echo "getInstance1: \n";
$urn1 = UniqueRandNumber::getInstance();
echo "getInstance1: \n";
$urn2 = UniqueRandNumber::getInstance();
echo "\n";
echo $urn1->fetch()."\n";
echo $urn2->fetch()."\n";
输出
getInstance1:
construct
getInstance1:
73
73
可以看出,两次getInstance()只调用了一次构造函数。
数据库连接单例模式
对于数据库连接,一个例子是
<?php
class Database
{
private $_db;
static $_instance;
private function __construct()
{
$dsn = '';
$this->_db = new PDO($dsn);
}
private function __clone()
{
}
public static function getInstance()
{
if(!(self::$_instance instanceof self)){
self::$_instance = new self();
}
return self::$_instance;
}
public function getPDO()
{
return $this->_db;
}
}
$database = Database::getInstance();
$pdo = $database->getPDO();
参考
- PHP高级程序设计-模式、框架和测试,Kevin,P17
- clone介绍(php5对象复制、clone、浅复制与深复制)