设计模式:单例模式
前言
学习了这么长的时间,设计模式却还是浅尝辄止,而且最近在回顾Vue知识的时候,发现了很多常见的设计模式:观察者啊,单例啊什么的。让我意识到,如果想要去读懂Vue底层的话,设计模式的思想是必不可少的。
这就从简单的单例模式开始,学习学习设计模式
什么是单例模式
字面上来看,就是只有一个实例的模式。简单来说,单例模式就是在整个系统中,保持一个实例,不生成新实例的模式。当然并不是说仅仅只能new
一个对象出来,而是说,针对某个特殊的对象,我们在第一次将它实例化后,以后再次访问时,访问到的一直都是之前实例化好的特殊对象。
为什么需要单例模式
正如上面说的那样,单例模式针对的是某些特殊的对象,那么该对象为什么被针对呢?
需要单例模式实现的对象或系统,一般具有如下特点:
对象
- 需要频繁的被创建、销毁,但在这些过程中无法优化
- 需要比较多的资源:读取配置、产生其他依赖对象
环境
- 希望节省内存
- 希望节省系统性能的开销
- 希望某些资源不被多重占用
- 希望有一个能够统一处理资源和优化的对象
Vuex
和Spring
中的Bean
对象就是如此
通过单例模式,我们能够简单的实现节省性能开销、节省内存、统一管理资源
等功能。因为只有一个实例对象存在,并不会新生成。
如何实现单例模式
单例模式的实现要点
- 保证对象只有一个,不能生成新对象出来
JavaScript
实现的条件还是不变,保证对象只有一个,不能生成新对象出来。
在JavaScript中,如果要实现私有化,首先想到的就是闭包了,闭包还能够使该实例不消失。
其次结合if判断语句,如果实例存在,直接返回,不存在就执行new运算符初始化实例。
闭包实现
1 | let SingleCase = (function Singleton (){ |
其实js中window顶层对象就是个单例了。
Es6 Class实现
首先我们要明白,Class是一个语法糖,本质上也是一个构造函数。
回顾单例的要求,一是共享一个实例,二是不能生成新实例出来
构造函数 constructor
Class有一个特点,在我们实例化Class时,会自动执行其内的constructor函数(每一个Class都有该函数,不声明默认为空。),可以初始化一些配置。
1 | class Singleton{ |
上面的方法能够实现单例的要点在于,只有第一个new Singleton()
是真真的new出来的,之后的其实都是第一个new出来的实例对象。
通过以下输出可以证明
1 | class Singleton { |
之所以该实例能够一直存在,是因为不仅仅后续创建的变量在引用它,而且Singleton本身的属性instance也在引用它,所以不会被销毁
静态方法/属性
在Es6中,Class提供了静态方法。表示该方法为该类独有,不会被实例继承。我们可以通过静态方法来实现单例
1 | // 静态方法 |
静态属性指的是 Class 本身的属性,即Class.propName
,而不是定义在实例对象(this
)上的属性
1 | // 静态属性(不是很确定这么用行不行,但是也能实现) |
Vuex的实现思路
by——Controllerszzy
1 | class SingleState { |
使用过程中通过 Vue.use(Vuex) 安装了Vuex插件,而Vuex 插件是一个对象,它在内部实现了一个 install 方法,这个方法会在插件安装时被调用,从而把 Store 注入到Vue实例里去。也就是说每 install 一次,都会尝试给 Vue 实例注入一个 Store。
单例模式的优劣总结
优点
- 减少了内存开支
- 减少了系统的性能开销
- 避免对资源的多重占用
- 设置全局的访问点, 优化和共享资源访问
缺点
- 扩展很困难
- 对测试是不利的
- 与单一职责原则有冲突
- 一个类应该只实现一个逻辑, 而不关心它是否是单例的, 是不是要单例取决于环境, 单例模式把“要单例”和业务逻辑融合在一个类中
对ES6中新增类型Symbol的疑问
Symbol的实现或者用法和单例有什么联系呢?感觉上挺像的,虽然只是用于防止命名冲突。Es6一直不太会用出来,所以理解的也不是很到位。
总结
其实单例模式还区分饿汉和懒汉两种模式,其差别在于:
饿汉要求直接生成实例,不管有没有调用该对象
懒汉要求什么时候用到了,什么时候再生成这个实例对象
补充
学习了js一段时间后,发现之前js实现的单例是比较局限的,自己并没有从一个前端的角度去思考设计模式如何实现。所以在阅读了《JavaScript设计模式与开发实践》后,来补充下最新的理解。
上述闭包实现的单例存在着一些问题
单例的判断逻辑和单例对象的创建混杂在了一起。假如想去创建其他非单例对象时,需要重新在再写一部分相同的创建逻辑,所以,我们需要把二者拆分开,以实现 单一职责原则
1 | // 单例逻辑的控制 |