参考博客1
参考博客2
单例模式
- 定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 实现:vuex,store对象就是一个单例对象
工厂模式
- 定义:由一个工厂类来决定创建某一种产品对象类的实例,主要用来创建同一类对象。到工厂里获取需要的功能,无需关注创建过程。
- 实现
点菜程序如下:
class Restaurant {
constructor() {
this.menuData = {}
}
getMenu(menu) {
if (!this.menuData[menu]) {
throw new Error('这个菜本店没有')
}
const { type, message } = this.menuData[menu]
return new Menu(type, message)
}
addMenu(menu, type, message) {
if (this.menuData[menu]) {
console.Info('已经有这个菜了!')
return
}
this.menuData[menu] = { type, message }
}
removeMenu(menu) {
if (!this.menuData[menu]) return
delete this.menuData[menu]
}
}
class Menu {
constructor(type, message) {
this.type = type
this.message = message
}
eat() {
console.log(this.type + this.message)
}
}
const restaurant = new Restaurant()
restaurant.addMenu('YuXiangRouSi', '鱼香肉丝', ' 真香')
restaurant.addMenu('GongBaoJiDin', '宫保鸡丁', ' 让我想起了外婆做的菜')
const dish1 = restaurant.getMenu('YuXiangRouSi')
dish1.eat()
const dish2 = restaurant.getMenu('HongSaoPaiGu')
策略模式
- 定义:定义一系列的算法,把它们一个个封装起来,并且使其可以相互替换
- 核心
至少由两部分组成:
第一个部分是一组策略类,封装了具体的算法,负责具体的计算过程;
第二个部分是环境类,接收客户的请求,然后把请求委托给某一个策略类 - 实现
(1) 通过绩点等级来计算学生的最终得分
(2) 表单提交校验 - 总结
优点: 可以有效地避免多重条件语句,将一系列方法封装起来有利于理解和维护
缺点: 往往策略集会比较多,需要事先了解定义好所有的情况
代理模式
- 定义:为对象提供一种代理,以控制对它的访问
- 实现(主要有三种)
保护代理(无值时拒绝访问主体)
虚拟代理(访问主体前进行一些额外的操作,比如说图片懒加载)
缓存代理(为一些开销大的运算结果提供暂时的缓存,提升效率)
发布订阅者模式
- 定义:也称作观察者模式,定义了对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
发布者->事件中心<=>订阅者。订阅者向事件中心订阅指定的事件->发布者向事件中心发布指定事件内容->事件中心通知订阅者,订阅者收到消息(可能有多个订阅者)。 - 实现:微博;vue框架;EventBus实现;promise实例
class EventBus {
constructor() {
this.handlerBus = {}
}
$on(eventName, handler) {
if (!this.handlerBus.hasOwnProperty(eventName)) {
this.handlerBus[eventName] = []
}
this.handlerBus[eventName].push(handler)
}
$emit(eventName, handlerParams) {
if (!this.handlerBus.hasOwnProperty(eventName)) {
throw new Error('未注册该事件')
}
const eventHandlers = this.handlerBus[eventName]
for (let i = 0; i < eventHandlers.length; i++) {
eventHandlers[i](handlerParams)
}
}
$onece(eventName, handlerParams) {
this.$emit(eventName, handlerParams)
this.$remove(eventName)
}
$remove(eventName, handler) {
if (!this.handlerBus.hasOwnProperty(eventName)) {
throw new Error('未注册该事件')
}
if (!handler) {
Reflect.deleteProperty(this.handlerBus, eventName)
return
}
const eventHandlers = this.handlerBus[eventName]
const handlerIndex = eventHandlers.findIndex((event) => event === handler)
if (handlerIndex === -1) {
throw new Error('未绑定该事件')
}
this.handlerBus[eventName].splice(handlerIndex, 1)
if (this.handlerBus[eventName].length === 0)
Reflect.deleteProperty(this.handlerBus, eventName)
}
}
export default new EventBus()
import bus from '@bus'
const f1 = (p) => {
console.log('f1-->', p)
}
const f2 = (p) => {
console.log('f2-->', p)
}
bus.$on('event1', f1)
bus.$on('event1', f2)
bus.$remove('event1', f1)
bus.$emit('event1', { a: 1 })
- 总结
优点:
(1) 时间上的解耦:注册的订阅行为由消息的发布方来决定何时调用,订阅者不用持续关注,发布者会负责通知;
(2) 对象上的解耦 :发布者不用提前知道消息的接受者是谁,发布者只需要遍历处理所有订阅该消息类型的订阅者发送消息即可,因此解耦了发布者和订阅者之间的联系
缺点:
(1) 创建订阅者会消耗一定的时间和内存,订阅的处理函数不一定会被执行,驻留内存有性能开销
(2) 弱化了对象之间的联系,复杂情况下可能会导致程序难以理解和跟踪维护
观察者模式
- 定义:目标<=>观察者。观察者观察目标(监听目标)->目标发生变化,主动通知观察者。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当目标对象状态发生变化时,会通知所有观察者对象,并使其自动更新。 - 实现:可以用报纸期刊的订阅来形象地说明,有多少人订阅报纸,报社就会发多少份报纸,报社和订报纸的客户就是上述“一对多”的依赖关系。
二者区别
- 角色角度来看,发布订阅者模式需要三种角色,发布者、事件中心和订阅者;观察者模式需要两种角色,目标和观察者,无事件中心负责通信。
- 从耦合度上来看,发布订阅者是一个事件中心调度模式,订阅者和发布者是没有直接关联的,通过事件中心进行关联,两者是解耦的;而观察者模式中目标和观察者是直接关联的,耦合在一起(有些观念说观察者是解耦,解耦的是业务代码,不是目标和观察者本身)。
二者优缺点
- 发布订阅模式
优点: 灵活,由于发布者和订阅者是解耦的,只要引入事件中心,无论在何处都可以发布订阅
缺点 : a. 使用不当会造成数据流混乱,容易导致代码不好维护;b. 需要维护事件列队,订阅的事件越多,内存消耗越大 - 观察者模式
优点: 响应式
缺点: 不灵活
观察者模式是不是发布订阅模式
- 网上关于这个问题的回答,出现了两极分化。其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构。
- 如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。
- 不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,在使用时应该根据场景来判断选择哪个。