TypeScript设计模式(1):单例模式

893 阅读3分钟

单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

单例模式是创建型设计模式的一种,它的实现方式有很多种,尤其像在后端语言(如java)中,会涉及到多线程、多进程实例唯一的情况,就需要考虑到加锁等操作。而对于前端JavaScript而言,其本身就是执行在单线程上的,所以实现起来就相对简单一些。常见的一些实现方式:

单例的实现

1. 简单单列模式实现

如下代码(typescript),通过创建单例类Singleton实现单例模式,并满足如下条件

  1. 声明静态属性存储单例,这里不能使用非静态属性,原因是返回实例的接口方法getInstance是静态方法
  2. 私有的构造函数,阻止外部通过new的方式创建Singleton的实例
  3. 暴露返回单例的静态方法getInstance
/**
 * 简单单例模式
 */
class Singleton {
  // 1. 静态属性保存实例
  private static instance: Singleton;
  public name: string = '';

  // 2. 私有构造函数,阻止被实例化
  private constructor(name: string) {
    this.name = name;
  }

  public getName() {
    return this.name;
  }

  // 3. 获取单例的接口
  public static getInstance(name: string): Singleton {
    if (Singleton.instance === undefined) {
      Singleton.instance = new Singleton(name);
    }
    return Singleton.instance;
  }
}


// test ====================================
const ins1 = Singleton.getInstance('hello');
const ins2 = Singleton.getInstance('world');
console.log('ins1 === ins2 ?', ins1 === ins2); // ins1 === ins2 ? true
console.log(ins1.name, ins2.getName()); // hello hello

2. 通用单例模式实现

通用单例模式,更像是一个单例工厂,它可通过参数方式,将需要创建单例的类传入工厂,工厂负责返回一个获取该类单例的方法。代码实现也比较简单

/**
 * 通用单例模式实现
 */
function getInstance<T>(Cls: new (...args: any[]) => T) {
  let instance: T;
  return function(...args: any[]) {
    if (instance === undefined) {
      instance = new Cls(...args);
    }
    return instance;
  };
}


// test ====================================
class Foo {
    public name: string;
    constructor(name: string) {
      this.name = name;
    }

    public getName() {
      return this.name;
    }
}
const getFooInstance = getInstance(Foo);
const ins3 = getFooInstance('world');
const ins4 = getFooInstance('hello');
console.log('ins3 === ins4 ?', ins3 === ins4); // ins3 === ins4 ? true
console.log(ins3.name, ins4.getName()); // world world

通过以上代码可以看出,getInstance 函数接收一个类作为参数,内部以闭包的方式保留类的引用,并随匿名函数返回。当再次执行返回的函数时,即可得到该类的唯一实例,并可传入一些初始化参数(只有第一次的参数生效)。

单例的用处

从业务概念上,有些状态、数据、组件等在系统中只应该保存一份,就比较适合设计为单例类。比如,全局的配置信息,全局用户信息、全局弹窗组件等。

单例的优缺点

优点:

  • 划分独立命名空间,避免全局变量污染
  • 代码逻辑集中,业务高度内聚
  • 只创建一个实例,减少对象频繁创建、销毁的消耗

缺点:

  • 对 OOP 特性的支持不友好(单例模式内部自己创建实例,外部无法通过new来实例化,无法继承扩展)
  • 对代码的可测试性不友好(由于单例模式属硬编码的方式,会导致内部的接口、数据或依赖都无法很好模拟)