单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例类拥有一个私有构造函数,确保用户无法通过 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 {
protected static $instance = null;
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 extends Singleton {
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);
|