Singleton 单例模式

首先,先来看看一个简单、特殊的创造物件的模式。

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one "single" instance. This is useful when exactly one object is needed to coordinate actions across the system.

Singleton (单例模式) 限制了一个类别只能建立唯一的实例。

这是什麽意思呢?如果我们先来看看一般的类别,譬如先前的 BaseballPlayer,这里我们稍微简化一下,并且利用它产生两个实例 playerA 和 playerB

class BaseballPlayer {
  constructor() {}
}

const playerA = new BaseballPlayer()
const playerB = new BaseballPlayer()

很明显的 playerA 和 playerB 是两个不同的东西

console.log(playerA === playerB)            // false

那麽,我们要如何让一个类别只能建立一个唯一的实例,譬如

class BaseballPlayer {
  // do something
}

const playerA = new BaseballPlayer()
const playerB = new BaseballPlayer()

然後让 playerA 和 playerB 都会拿到同样的实例

console.log(playerA === playerB)            // true

在进入实作之前,先来看看这个模式想要解决什麽问题。

解决什麽问题?

集中管理

当使用者透过类别建立实例的时候,每个实例就是独立的个体,并可以各自执行各自的行为。

但如果我们想要「集中控管」使用者使用这个工具的行为,与其让使用者自己透过类别创造出各自新的工具,不如就想办法让使用者每次透过类别取得实例的时候,都取得「同一个工具」,也就会使用同一个工具。

对我们来说,我们只要控制这一个工具(实例),就等於控制了所有使用者手上的工具。

共享全域变数

另一方面,如果有一个变数是需要让所有实例共享的的话,那麽也可以透过这个方式,让每一位使用者能够藉由取得同一个实例来操作同一个变数。

使用场景

在系统当中,有些功能是高度共享的,如果这时候采用单例模式,就会便於管理。譬如 logger 或是 cache,不管是谁呼叫了这些实例或功能,都被集中管理,以避免不同实例各自操作所可能产生的 concurrent 问题

实作方式

要实作单例模式,有一些不同的方法,这里使用「静态类别」的方法来实作。

静态类别的特性是,可以不用建立实例就可以取用其方法。所以这里我们利用这个特性,不让使用者建立实例 (e.g., new Gov()),而是直接呼叫静态方法 getInstance 来取得实例。

所以在下面的程序码当中,如果使用者呼叫了 getInstance 但此时还没有任何实例,那麽就在这个方法内部建立新的实例。但如果实例已经存在,那麽就直接回传该实例。

class Gov {
  private static instance: Gov

  private constructor() { }

  public static getInstance(): Gov {
    if (!Gov.instance) {
        Gov.instance = new Gov()
    }

    return Gov.instance
  }
}

如此一来,就可以确保所有使用这个类别的使用者,都可以拿到同一个实例

const yourGov = Gov.getInstance()
const myGov = Gov.getInstance()

console.log(yourGov === myGov)        // true

优点与缺点

单例模式的优点是,藉由提供所有使用者同样的实例,让我们可以集中管理资料、操作行为。

但是缺点是,它违反了单一功能原则,因为他控制了

  • 确保只有一个实例的产生,以及
  • 实例的实作

也因为集中管理,导致扩展性不佳。如果有任何的新需求进入,都必须更改这个类别当中的实作方式。另一方面,也可能会隐藏了它与其他类别的关系,因为对於使用者来说,只要直接取用实例就行,但如果要理解功能是如何实作,就必须进入这个类别的程序码查看,无法透过相对外显的继承、参数传递等方式来更快理解。


<<:  如何使用WYSIWYG Python GUI 设计工具快速设计出子视窗及产出程序码呢?

>>:  DAY17 专案进度按钮功能实现-1

Day21-TypeScript(TS)的函式(Function) Part1

经过前两天的函示介绍相信大家对函式(Function)已经有一定程度的了解了吧, 那麽对於TS的函式...

冒险村09 - Time format config

09 - Time format 在专案中时常会有用到显示时间的地方,可能格式只有一种,但是会散落在...

【第二十九天 - Python 反序列化】

Q1. 什麽是 python 反序列化 当我们希望在 Python 中保存一个物件(例如将机器学习训...

Day15 Composer & Laravel - install

经过了两周的介绍,已经从古代慢慢走到了现代,也将环境准备的5566了,是时候该介绍我的工作核心:La...

#20-有看到我的猫吗?用offsetPath让猫猫滚起来!(SVG)

偶尔会看到,往下滚,球就会跟着滚动的幅度以抛物线移动。 今天就来使用SVG的Path做做看! 其实是...