单例模式
一,单列模式的介绍
1.概念
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例;
单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.示例场景
按照面向对象的编程思想,任何东西都可以抽象为一个类,然后可以new
出若干个对象。
但是针对某些场景,只能存在唯一的对象;
例如:jQuery
中的$; 登录;购物车等;
3.UML 类图如下
二,单例模式的应用
1.模式特性的说明
- 单例模式需要使用到
java
的特性(private
) ES6
中并没有使用private的关键字;(暂时还没有规范到ES6
中,但typeScript
除外)
1-1. java实例
单例模式用到了 java 的一些特性,而 es6
没有这些特性,所有这里使用 java
进行演示,代码如下:
public class SingleObject {
// 注意,私有化构造函数,外部不能 new ,只能内部能 new !!!!
private SingleObject(){
}
// 唯一被 new 出来的对象
private SingleObject instance = null;
// 获取对象的唯一接口
public SingleObject getInstance() {
if (instance == null) {
// 只 new 一次
instance = new SingleObject();
}
return instance;
}
// 对象方法
public void login(username, password){
System.out.println("login...");
}
}
使用代码
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的 !!!
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
object.login();
}
}
单例模式的关键在于不能让外部使用者 new
出对象,即构造函数是 private
;
这里的关键字样表示:
public
公有的
protect
受保护
private
私有
1-2. javaScript
的简单实例 - 存在不透明性
这里通过SingleObject.getInstance
来获取SingleObject
唯一的类,相对较简单,但存在一个问题;这个类的不透明性,SingleObject
使用者必须知道这是个单例类;
跟以往的 new XXX
的方式获取不同,这里使用SingleObject.getInstance
来获取对象;实例代码如下:
class SingleObject {
login() {
console.log('login...')
}
}
SingleObject.getInstance = (function () {
let instance
return function () {
if (!instance) {
instance = new SingleObject();
}
return instance
}
})()
// 测试:注意这里只能使用静态函数 getInstance ,不能 new SingleObject() !!!
let obj1 = SingleObject.getInstance()
obj1.login()
let obj2 = SingleObject.getInstance()
obj2.login()
console.log(obj1 === obj2) // 两者必须完全相等
1-3. 使用代码实现单例模式 - 完成一个透明的单例
这里使用到“单一职责的原则”,将创建对象
与对象的初始化方法
进行分离;
这里把负责管理单例的逻辑移到了代理类ProxySingletonObject
中,而实现单例的SingleObject
变成一个普通的类,结合起来实现单例的效果;代码实例如下:
// 实例的构造函数
class SingleObject {
login() {
console.log('login...');
}
}
// ProxySingletonObject 引入一个代理类
var ProxySingletonObject = (function(){
var instance;
return function(){
if (!instance){
instance = new SingleObject();
}
return instance;
}
})();
var login1 = new ProxySingletonObject()
var login2 = new ProxySingletonObject()
console.log(login1 === login2, "login1 === login2");
1-4. 通用的惰性单例
单列的逻辑:用一个变量来标志是否创建过对象,如已创建,下次直接返回已创建好的对象;
var obj;
if (!obj) {
obj = XXX;
}
这里将创建实例对象的职责
和管理单列的职责
分开;createLoginLayer
的方法可用于创建多种,随时替换方法;
// 创建对象的实例:
var createLoginLayer = function(){
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
// 单列的逻辑
var getSingle = function(fn) {
console.log(fn)
var result;
return function() {
return result || (result = fn.apply(this, arguments))
}
}
// 调用单列的逻辑创建createLoginLayer的单列
var createSingleLoginLayer = getSingle( createLoginLayer );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer(); // 登录框的createLoginLayer只有一个
loginLayer.style.display = 'block';
};
三,单列模式的场景
常使用中,很多都用到了单例的思想,但是不一定完全按照单例的类图来实现:
项目中的场景
jQuery
只有一个 $
在项目中应用jQuery
的文件,有且只有一个$
,其内部调整逻辑也类似:
// jQuery 只有一个 `$`
if (window.jQuery != null) {
return window.jQuery
} else {
// 初始化...
}
- 模拟实现一个登录框
- 购物车
- redux 和 vuex 的 store
单例模式 vs 单一职责原则
- 单一职责原则是针对所有的设计,单个功能尽量拆分,一个模块做好一个功能。如果做不好,会带来模块臃肿,不好维护。
- 单例模式是系统的有且只有一份数据,如果不这样做,会出
bug
;(这里当我们写自己的模块时,进行封装使用引入到项目中,也可多一次创建就可使用;避免多次创建带来不必要的损耗)
设计原则验证:
- 符合单一职责原则,只实例化唯一的对象
- 没法具体开放封闭原则,但是绝对不违反开放封闭原则