0%

单例模式-创建型

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例类拥有一个私有构造函数,确保用户无法通过 new 关键字直接实例化它。 除此之外,该模式中包含一个静态私有成员变量与公有的方法,该方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。

优点

单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。

由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。这将演变为多例模式

缺点

由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。

单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,将产品的创建和产品的本身的功能融合到一起。

滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出

现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失

在某些有反射机制情况下,可以通过反射进行实例化,从而打破单例。

适用

只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象

需要共享访问或共享数据

创建一个对象需要消耗的资源过多,如访问IO和数据库等资源

线程池实例、缓存实例、数据库连接实例、日志实例

应用

JDK 中的 java.lang.Runtime

1
Runtime singleton = Runtime.getRuntime();

PHP 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
/**
* Class Singleton
*/
class Singleton
{
/**
* @var null
*/
protected static $instance = null;

/**
* 实例化以及检查
*
* @return null|Singleton
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static;
}

return static::$instance;
}

/**
* 禁止调用构造函数
*/
protected function __construct() {}

/**
* 禁止复制
*/
protected function __clone() {}

/**
* 单例支持序列化与反序列化
*/
public function __wakeup()
{
self::$instance = $this;
}
}

/**
* 单例继承
*
* Class SubSingleton
*/
class SubSingleton extends Singleton
{
/**
* @var null
*/
protected static $instance = null;
}

$singleton1 = Singleton::getInstance();
$singleton2 = Singleton::getInstance();
var_dump($singleton1 === $singleton2);
var_dump($singleton1);

$a = Singleton::getInstance();
$a = unserialize(serialize($a));
var_dump($a === Singleton::getInstance());

// 继承
$subSingleton1 = SubSingleton::getInstance();
$subSingleton2 = SubSingleton::getInstance();
var_dump($subSingleton1 === $singleton1);
var_dump($subSingleton1 === $subSingleton2);

// 可以反射
$reflector = new ReflectionClass('Singleton');
$b = $reflector->newInstanceWithoutConstructor();
var_dump($b);