设计原则
单一职责原则(SRP)
一个对象或方法只做一件事情。如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。应当把方法或对象划分成较小的粒度
最少知识原则(LKP)
一个软件实体应当尽可能少地与其他实体发生相互作用。应当尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那两个对象就不要发生直接的相互联系,可以转交给第三方进行处理
开放-封闭原则(OCP)
软件实体(类、模块、函数)等应该是可以拓展的,但是不可修改
当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,尽量避免改动程序的源代码,防止影响源系统的稳定
一、单例模式
1、定义
保证一个类仅有一个实例,并提供一个访问他的全局访问点
2、核心
确保只有一个实例,并提供全局访问
3、实现
多次调用也仅设置一次,可以使用闭包的方式缓存一个内部变量来实现这个单例
4、目的
为了防止频繁的创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一实例对象。
单例模式的类型
懒汉模式:在真正需要使用对象时才去创建该单例
饿汉模式:在类加载是已经创建好该单例对象,等待被程序使用
懒汉模式创建单例对象
懒汉式创建对象的方式是在程序使用对象前,先判断该对象是否已经实例化(判空),若已经实例化直接返回该类对象。否则则先执行实例化操作。
var Singleton = (function(){
var instantiated;//比较懒,在加载时,不创建实例,因此类加载速度快,但运行时获取对象的速度慢
function init(){
return {
publicMethod:function(){
console.log('helloWorld')
},
publicProperty:3
}
}
return {
// 使用闭包保存变量
getInstance:function (){
if(!instantiated){
instantiated=init() //第一次调用时执行
}
return instantiated//返回实例对象
}
}
})()//使用立即执行函数在类加载时执行
//可在其他类调用公有的方法或属性来获取实例
Singleton.getInstance().publicMethod()
// 再次执行的时候也是执行的第一次init()方法
Singleton.getInstance().publicMethod()
Singleton.getInstance().publicProperty=4
console.log(Singleton.getInstance().publicProperty)
饿汉模式
饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
var Singleton1 = (function (){
//比较饥饿,在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快
var instantiated = init();
function init(){
return {
publicMethod:function (){
console.log('helloWorld')
},
publicProperty:3
}
}
return {
getInstance:function (){
return instantiated
}
}
})()
二、发布订阅
定义
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都将得到状态改变的通知
实现思路
创建一个对象
在该对象上创建一个缓存列表(即调度中心)
on方法用来把函数fn都加到缓存队列中(订阅者注册事件到调度中心)
emit方法取到arguments里第一个当做event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理订阅者订阅时的方法)
off方法可以根据event值取消订阅
once方法值监听依次,调用完毕后删除缓存函数
demo实现on和emit方法
//调度中心
let eventEmitter = {}
//缓存列表,存放event及fn
eventEmitter.list={}
//订阅
eventEmitter.on = function (event,fn){
let _this = this;
//如果对象中没有对应的event值,也就是说明没有订阅过,就给event创建个缓存列表
//如果对象中有相应的event值,把fn添加到对应的event的缓存列表里
(_this.list[event] || (_this.list[event]=[])).push(fn)
return _this
}
eventEmitter.emit = function (){
let _this = this;
//第一个参数是对应的event值,直接用数组的shift方法取出
let event = [].shift.call(arguments),
fns = [...this.list[event]]
// 该事件注册的所有方法 fns
console.log(event,'事件名')
if(!fns || fns.length === 0){
return false
}
//遍历event值对应的缓存列表,依次执行fn
fns.forEach(fn=>{
fn.apply(_this,arguments)
})
return _this
}
function user1(content){
console.log('用户1订阅了:',content)
}
function user2(content){
console.log('用户2订阅了',content)
}
//订阅
eventEmitter.on('article',user1)
eventEmitter.on('article',user2)
// 发布
eventEmitter.emit('article','javascript 发布-订阅模式')
观察者模式和发布订阅模式的区别
观察者模式:观察者直接订阅主题,而当主题被激活的时候,会触发观察者里的事件
发布订阅模式:订阅者吧自己想订阅的事件注册到调度中心,发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度,订阅者注册到调度中心的处理代码
差异
在观察者模式中,观察者是知道subject的,subject一直保持对观察者进行记录。然而在发布订阅者模式中发布者和订阅者不知道对方的存在。
他们只有通过消息代理进行通信
在发布订阅模式中,组件是松耦合的,正好和观察者模式相反
观察者模式大多数时候是同步的,比如当事件触发,Subject 就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。
三、装饰器模式
定义:装饰器模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能