佳星辰 的个人博客

记录精彩的程序人生

努力、自律、上进
早睡、锻炼、读书
  menu
9 文章
0 浏览
0 当前访客
ღゝ◡╹)ノ❤️

单例模式

单例模式

意图

《设计模式》对单例模式意图的描述如下:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

从意图来看,单例模式的两个核心为「保证一个类仅有一个实例」以及 「提供一个访问它的全局访问点」。

为什么要保证一个类仅有一个实例?有时候是为了控制某些共享资源,例如一个系统仅有一个打印机、文件系统和窗口管理器。

如何保证一个类仅有一个实例?首先,通过构造函数来获取实例肯定是不行的,因为构造函数每次都会生成新的实例对象。其次,单单提供一个全局变量来访问实例也不够,因为不能保证获取的是唯一实例。

因此,更好的解决方式就是让类自身来负责保存它的唯一实例,并提供一个访问该实例的方法。这样的话,对于客户端而言,每次获取的都是同一个实例。不过,类既负责了实例的保存,也负责了实例的访问,违反了 单一职责 原则。

实现

单例模式的实现可以分为两种,在类中直接实现单例模式,或者通过继承的方式来实现单例模式。

在类中实现单例模式

第一种方法就是我们在类中直接实现单例模式,这也是最常用的方法

<?php

final class Singleton
{
    // 保存实例
    private static $instance;

    // 第一次获取时创建
    public static function getInstance(): Singleton
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    // 防止通过构造函数实例化
    private function __construct(){}

    // 防止被克隆
    private function __clone(){}

    // 防止被反序列化
    private function __wakeup(){}
}

通过继承实现单例模式

另外一种方式就是定义一个全局的单例类,子类只需要继承该类就可以实现单例模式。

<?php

class Singleton
{    
    // 保存一个或多个实例
    private static $instances = [];

    protected function __construct(){}

    protected function __clone(){}

  public function __wakeup()
  {
    throw new \Exception("不能反系列化单例");
  }

  public static function getInstance(): Singleton
  {
    $subclass = static::class;

    if (!isset(self::$instances[$subclass])) {
      self::$instances[$subclass] = new static();
    }

    return self::$instances[$subclass];
  }

}

// 测试
class Foo extends Singleton
{
}

Foo::getInstance() === Foo::getInstance(); // true

总结

当你要控制某些共享资源时,可使用单例模式。但是单例模式也存在几点不足

  • 单例模式同时负责类的保存与访问,违反了「单一职责」原则,因此,单例模式被认为是反模式。
  • 在多线程环境中,需要对单例模式进行特殊处理,防止多个线程同时创建单例对象。
  • 由于单例模式不能被实例化,将导致单元测试难以进行。

标题:jxc321
作者:jxc
地址:https://jxc321.com/articles/2023/07/01/1688183538700.html