创建型模式:单例模式
简介
所谓的的单例设计模式,就是采取一定的方法保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
以前端中vuex为例,它在整个vue的系统中,获取到的就是一个全局唯一的对象实例。由于vuex本身就是一个重量级的功能,且一个项目通常只需要一个vuex状态管理就够了,所以采用的单例模式。
单例模式在java中有八种方法:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式 (线程不安全)
- 懒汉式 (线程安全,同步方法)
- 懒汉式 (线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
但是前端中其实好多方法就做不到,因为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 工厂等);
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据