单例模式多是设计模式里面最简单的模式了,虽然简单,但在咱们平常生活和编程中却常常接触到,本节咱们一块儿来学习一下。
单例模式 (Singleton Pattern)又称为单体模式,保证一个类只有一个实例,并提供一个访问它的全局访问点。也就是说,第二次使用同一个类建立新对象的时候,应该获得与第一次建立的对象彻底相同的对象。
在相似场景中,这些例子有如下特色:
function ManageGame(){ if(ManageGame._schedule){ // 判断是否已经有单例了 return ManageGame._schedule } ManageGame._schedule = this } ManageGame.getInstance = function(){ if(ManageGame._schedule){ // 判断是否已经有单例了 return ManageGame._schedule } return ManageGame ._schedule =new ManageGame() } const schedule1 = new ManageGame() const schedule2 =ManageGame.getInstance() console.log(schedule1===schedule2)
ts的 class 改造
class ManageGame{ private static schedule: any = null; static getInstance() { if (ManageGame.schedule) { // 判断是否已经有单例了 return ManageGame.schedule } return ManageGame.schedule = new ManageGame() } constructor() { if (ManageGame.schedule) { // 判断是否已经有单例了 return ManageGame.schedule } ManageGame.schedule = this } } const schedule1 = new ManageGame() const schedule2 = ManageGame.getInstance() console.log(schedule1 === schedule2)// true
缺点:上面方法的缺点在于维护的实例做为静态属性直接暴露,外部能够直接修改。
根据上面的例子提炼一下单例模式,游戏能够被认为是一个特定的类(Singleton),而存档是单例(instance),每次访问特定类的时候,都会拿到同一个实例。主要有下面几个概念:
const Singleton = (function() { let _instance = null // 存储单例 const Singleton = function() { if (_instance) return _instance // 判断是否已有单例 _instance = this this.init() // 初始化操做 return _instance } Singleton.prototype.init = function() { this.foo = 'Singleton Pattern' } return Singleton })() const visitor1 = new Singleton() const visitor2 = new Singleton() console.log(visitor1 === visitor2) // true
const Singleton = (function() { let _instance = null // 存储单例 const Singleton = function() { if (_instance) return _instance // 判断是否已有单例 _instance = this this.init() // 初始化操做 return _instance } Singleton.prototype.init = function() { this.foo = 'Singleton Pattern' } Singleton.getInstance = function() { if (_instance) return _instance _instance = new Singleton() return _instance } return Singleton })() const visitor1 = new Singleton() const visitor2 = new Singleton() // 既能够 new 获取单例 const visitor3 = Singleton.getInstance() // 也能够 getInstance 获取单例 console.log(visitor1 === visitor2) // true console.log(visitor1 === visitor3) // true
let getInstance { let _instance = null // 存储单例 const Singleton = function() { if (_instance) return _instance // 判断是否已有单例 _instance = this this.init() // 初始化操做 return _instance } Singleton.prototype.init = function() { this.foo = 'Singleton Pattern' } getInstance = function() { if (_instance) return _instance _instance = new Singleton() return _instance } } const visitor1 = getInstance() const visitor2 = getInstance() console.log(visitor1 === visitor2)
以前的例子中,单例模式的建立逻辑和原先这个类的一些功能逻辑(好比 init 等操做)混杂在一块儿,根据单一职责原则,这个例子咱们还能够继续改进一下,将单例模式的建立逻辑和特定类的功能逻辑拆开,这样功能逻辑就能够和正常的类同样。
/* 功能类 */ class FuncClass { constructor(bar) { this.bar = bar this.init() } init() { this.foo = 'Singleton Pattern' } } /* 单例模式的赋能类 */ const Singleton = (function() { let _instance = null // 存储单例 const ProxySingleton = function(bar) { if (_instance) return _instance // 判断是否已有单例 _instance = new FuncClass(bar) return _instance } ProxySingleton.getInstance = function(bar) { if (_instance) return _instance _instance = new Singleton(bar) return _instance } return ProxySingleton })() const visitor1 = new Singleton('单例1') const visitor2 = new Singleton('单例2') const visitor3 = Singleton.getInstance() console.log(visitor1 === visitor2) // true console.log(visitor1 === visitor3) // true
/* Person 类 */ class Person { constructor(name, age) { this.name = name this.age = age } } /* 单例模式的赋能方法 */ function Singleton(FuncClass) { let _instance return new Proxy(FuncClass, { construct(target, args) { return _instance || (_instance = Reflect.construct(FuncClass, args)) // 使用 new FuncClass(...args) 也能够 } }) } const PersonInstance = Singleton(Person) const person1 = new PersonInstance('张小帅', 25) const person2 = new PersonInstance('李小美', 23) console.log(person1 === person2) // true
有时候一个实例化过程比较耗费性能的类,可是却一直用不到,若是一开始就对这个类进行实例化就显得有些浪费,那么这时咱们就可使用惰性建立,即延迟建立该类的单例。以前的例子都属于惰性单例,实例的建立都是 new 的时候才进行。前端
惰性单例又被成为懒汉式,相对应的概念是饿汉式:vue
class FuncClass { constructor() { this.bar = 'bar' } } // 饿汉式 const HungrySingleton = (function() { const _instance = new FuncClass() return function() { return _instance } })() // 懒汉式 const LazySingleton = (function() { let _instance = null return function() { return _instance || (_instance = new FuncClass()) } })() const visitor1 = new HungrySingleton() const visitor2 = new HungrySingleton() const visitor3 = new LazySingleton() const visitor4 = new LazySingleton() console.log(visitor1 === visitor2) // true console.log(visitor3 === visitor4) // true
ts实现
懒汉式单例react
class LazySingleton{ private static instance:LazySingleton = null; private constructor(){ //private 避免类在外部被实例化 } public static getInstance():LazySingleton{ if (LazySingleton.instance == null) { LazySingleton.instance = new LazySingleton(); } return LazySingleton.instance; } someMethod() {} } let someThing = new LazySingleton(); // Error: constructor of 'singleton' is private let instacne = LazySingleton.getInstance(); // do some thing with the instance
用懒汉式单例模式模拟产生美国当今总统对象。
分析:在每一届任期内,美国的总统只有一人,因此本实例适合用单例模式实现,图 2 所示是用懒汉式单例实现的结构图。
class SingletonLazy{ public static main(arg) { let zt1 = President.getInstance(); zt1.getName(); let zt2 = President.getInstance(); zt2.getName(); if (zt1 === zt2) { console.log("他们是同一我的"); } else { console.log("他们不是同一人"); } } } class President{ private static instance: President = null; private constructor() { console.log("产生一个总统了"); } public static getInstance():President{ if (President.instance == null) { President.instance = new President(); } else { console.log("已经有了一个总统了,不能产生新总统!"); } return President.instance; } public getName():void { console.log("我是美国总统:特朗普。"); } }
namespace 饿汉式{ class HungrySingleton{ private static instance: HungrySingleton = new HungrySingleton(); private constructor() { } public static getInstance(): HungrySingleton{ return HungrySingleton.instance; } } let someThing = new HungrySingleton(); // Error: constructor of 'singleton' is private let instacne = HungrySingleton.getInstance(); // do some thing with the instance }
以 ElementUI 为例,ElementUI 中的全屏 Loading 蒙层调用有两种形式:
// 1. 指令形式 Vue.use(Loading.directive) // 2. 服务形式 Vue.prototype.$loading = service
用服务方式使用全屏 Loading 是单例的,即在前一个全屏 Loading 关闭前再次调用全屏 Loading,并不会建立一个新的 Loading 实例,而是返回现有全屏 Loading 的实例。vue-router
下面咱们能够看看 ElementUI 2.9.2 的源码是如何实现的,为了观看方便,省略了部分代码:
import Vue from 'vue' import loadingVue from './loading.vue' const LoadingConstructor = Vue.extend(loadingVue) let fullscreenLoading const Loading = (options = {}) => { if (options.fullscreen && fullscreenLoading) { return fullscreenLoading } let instance = new LoadingConstructor({ el: document.createElement('div'), data: options }) if (options.fullscreen) { fullscreenLoading = instance } return instance } export default Loading
单列模式的特色
单例模式主要解决的问题就是节约资源,保持访问一致性。
简单分析一下它的优势:
单例模式也是有缺点的
单例模式的使用场景那咱们应该在什么场景下使用单例模式呢: