我们先来看一下例子:
<?php
class Test {
public function __construct() {
echo "init\n";
}
}
$test1 = new Test();
$test2 = new Test();
$test3 = new Test();
echo var_dump($test1 === $test2);
这段代码将会输出:
init
init
init
bool(false)
看起来没啥问题,实例化了三次类,符合正常的逻辑。但是如果用在某些特殊场景,比如:db、redis…等等类中,这就意味着每调用一次里面的方法都会重新连接一下库,造成过多的连接浪费。这里呢,就需要用到单例这个设计模式。
单例模式最多只能拥有一个该类的实例存在,一旦创建就会一直在内存中。特点如下:
- 单例模式不能直接实例化创建,只能由类自身实例化。(所以构造函数可以设置为私有)
- 需要有一个能访问到的实例公开的静态方法和一个私有静态成员变量来保存类实例
- 类中通常需要有一个空的私有__clone方法防止对单例类进行实例克隆
示例代码如下:
<?php
class Test {
// 对象实例
private static $instance;
// 单例只能自身实例化 最好设置为私有
public function __construct() {
echo "init\n";
}
// 实例化
public static function instance($options = []) {
if (is_null(self::$instance)) {
self::$instance = new static($options);
}
return self::$instance;
}
// 防止对单例类进行实例克隆
private function __clone(){
}
}
$test1 = Test::instance();
$test2 = Test::instance();
$test3 = Test::instance();
echo var_dump($test1 === $test2);
输出结果如下:
init
bool(true)
构造函数只执行了一次,说明类只被实例化了一次,下面再详细比较一下:
<?php
class Test {
// 对象实例
private static $instance;
// 单例只能自身实例化 最好设置为私有
public function __construct() {
}
// 实例化
public static function instance($options = []) {
if (is_null(self::$instance)) {
self::$instance = new static($options);
}
return self::$instance;
}
// 防止对单例类进行实例克隆
private function __clone(){
}
}
$test1 = new Test();
$test2 = new Test();
var_dump($test1 === $test2);
$test3 = Test::instance();
$test4 = Test::instance();
var_dump($test3 === $test4);
var_dump($test1);
var_dump($test2);
var_dump($test3);
var_dump($test4);
输出结果如下:
bool(false)
bool(true)
object(Test)#1 (0) {
}
object(Test)#2 (0) {
}
object(Test)#3 (0) {
}
object(Test)#3 (0) {
}
从这个结果中肯定又可以看到了,输出的每个对象后面都包含一个数字(对象的标识符,也叫句柄、引用、指针),通过单例模式方法调用的对象句柄是一样的,说明类没有被重复调用,使用了单例模式。
大家可能在其他版本的单例模式介绍中,也有看到实例化代码是下面这两种写法也有
self::$instance = new self();
self::$instance = new static();
无论哪种写法都是给单身的你new了一个对象,如果类没有被继承两种方法其实没有啥区别的, 被继承才能显现出来区别。介绍一下这两种方法:
self:和self关键词类似,指向类本身,意思就是写在那个类中,就返回那个类的实例,如果有子类继承了该类,返回的是该自身的实例
static::不被解析为当前定义方法所在的类, 由调用者决定,如果有子类继承了该类,返回该子类的实例