单例模式

46 阅读3分钟

问题

单例模式同时解决了两个问题, 所以违反了_单一职责原则_:

  1. 保证一个类只有一个实例

222.png 2. 为该实例提供一个全局访问节点。 还记得你 (好吧, 其实是我自己) 用过的那些存储重要对象的全局变量吗? 它们在使用上十分方便, 但同时也非常不安全, 因为任何代码都有可能覆盖掉那些变量的内容, 从而引发程序崩溃。

真实世界类比

政府是单例模式的一个很好的示例。 一个国家只有一个官方政府。 不管组成政府的每个人的身份是什么, ​ “某政府” 这一称谓总是鉴别那些掌权者的全局访问节点。

单例模式结构

333.png

  1. 单例 (Sin­gle­ton) 类声明了一个名为 get­Instance获取实例的静态方法来返回其所属类的一个相同实例。

    单例的构造函数必须对客户端 (Client) 代码隐藏。 调用 获取实例方法必须是获取单例对象的唯一方式。

单例模式适合应用场景

如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。

单例模式禁止通过除特殊构建方法以外的任何方式来创建自身类的对象。 该方法可以创建一个新对象, 但如果该对象已经被创建, 则返回已有的对象。

如果你需要更加严格地控制全局变量, 可以使用单例模式。

单例模式与全局变量不同, 它保证类只存在一个实例。 除了单例类自己以外, 无法通过任何方式替换缓存的实例。

请注意, 你可以随时调整限制并设定生成单例实例的数量, 只需修改 获取实例方法, 即 getInstance 中的代码即可实现。

与其他模式的关系

  • 外观模式类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。
  • 如果你能将对象的所有共享状态简化为一个享元对象, 那么享元模式就和单例类似了。 但这两个模式有两个根本性的不同。
    1. 只会有一个单例实体, 但是享元类可以有多个实体, 各实体的内在状态也可以不同。
    2. 单例对象可以是可变的。 享元对象是不可变的。
  • 抽象工厂模式、 生成器模式原型模式都可以用单例来实现。

es5

function Dialog(title) {
    this.title = title;
}

Dialog.prototype.getTitle = function () {
    console.log("getTitle", this.title);
};

let CreateSingle = function (Constructor) {
    let instance;
    let SingleConstructor = function () {
        if (!instance) {
            instance = new Constructor(...arguments);
        }
        return instance;
    };
    return SingleConstrctor;
};

let Single1 = CreateSingle(Dialog);
let s1 = Single1('xxx');
let s2 = Single1();
s1.getTitle()
console.log("===", s1 === s2);

es6


class Singleton {
    private static instance: Singleton;
	// 私有化构造函数 不能 new
    private constructor() { }

    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}


function clientCode() {
    const s1 = Singleton.getInstance();
    const s2 = Singleton.getInstance();
    // s1 === s2
}

clientCode();