单例模式-创建型

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

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

优点

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

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

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

缺点

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

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

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

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

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

适用

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

  • 需要共享访问或共享数据

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

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

应用

JDK 中的 java.lang.Runtime

Runtime singleton = Runtime.getRuntime();

示例

<?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);

输出:

bool(true)
object(Singleton)#1 (0) {
}

bool(true)

bool(false)
bool(true)

object(Singleton)#5 (0) {
}