简介

所谓的的单例设计模式,就是采取一定的方法保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

以前端中vuex为例,它在整个vue的系统中,获取到的就是一个全局唯一的对象实例。由于vuex本身就是一个重量级的功能,且一个项目通常只需要一个vuex状态管理就够了,所以采用的单例模式。

单例模式在java中有八种方法:

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式 (线程不安全)
  4. 懒汉式 (线程安全,同步方法)
  5. 懒汉式 (线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举

但是前端中其实好多方法就做不到,因为js是单线程的,且并不是强类型语言,所以其中:双重检查,静态代码块,枚举,静态内部类,等都是用不了的。

所以我们只需要了解一下懒汉式和饿汉式,以及扩展一下在多线程中饿汉式的一些问题。

饿汉式

在js和ts中都没有静态代码块的概念,所以我们使用的是静态常量。

class One {
  private static instance: One = new One();
  private constructor() {}

  public static getInstance(): One {
    return One.instance;
  }
}

首先我们需要私有化类的构造器,以防止外部可以通过new关键字来创建实例。

通过内部私有的静态属性保存一个自身的实例对象。

对外抛出一个静态的方法getInstance,用于获取实例化对象。

外部使用只能通过抛出的方法获取实例并使用:

const instance = One.getInstance();

优缺点

优点

写法简单,在类被加载时就初始化好了实例,避免了在多线程中同步的问题。

缺点

并没有达到Lazy Loading 懒加载的效果,从未使用的情况下会在内存中产生一个实例,造成内存的浪费。

使用时,我们应该明确一点,如果这个实例一定会被使用,那么就可以直接使用这种模式。

懒汉式

懒汉式就是在第一次使用时再创建实例,后续情况直接抛出已经实例化好的对象。

class One {
  private static instance: One;
  private constructor() {}

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

const instance = One.getInstance();

优缺点

优点

达到了懒加载的效果

缺点

但是该方式只能再单线程下使用。

好在js就是单线程的,所以放心大胆的用。

懒汉式多线程知识扩展

当我们使用多线程的时候,比如有a、b两个线程,他们都同时调用了getInstance方法,此时他们都会进入到if (!One.instance)的判断中去,因为那时实例对象并不存在,所以进入该判断是理所当然的。

于是乎就会造成两次实例化,这显然会有问题。

在java中,我们可以通过synchronized修饰符来标明getInstance方法必须是一个同步方法,使用了该修饰符,哪怕a、b同时调用,也一定会等待上一个人调用结束再调用。

伪代码(假的,无法执行):

class One {
  private static instance: One;
  private constructor() {}

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

但是这种用法会产生一个性能损耗,因为getInstance变成了一个永久的同步方法,在第一次初始化完后,后面的再去调用,还是会走单线程,这显然性能上就不太理想了。

于是乎在java中又有了几种做法,比如:双重检验,静态内部类、枚举,都能更好的实现多线程中的懒汉式。

看个java的双重检验例子:

class One {
  private static volatile instance One;

  private One() {}

  public static synchronized One getInstance(){
    if(instance == null){
      synchronized(One.class) {
        if(instance == null){
          instance = new One();
        }
      }
    }

    return instance;
  }
}

总结

单例模式保证了系统内只存在一个对象实例,节省了系统资源,适用于需要频繁的进行创建和销毁的对象,创建对象时又耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如:数据源、session 工厂等);

分类: 设计模式 标签: 设计模式单例模式创建型模式

评论

暂无评论数据

暂无评论数据

目录