js常见的设计模式

参考博客1

参考博客2

单例模式

  1. 定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
  2. 实现:vuex,store对象就是一个单例对象

工厂模式

  1. 定义:由一个工厂类来决定创建某一种产品对象类的实例,主要用来创建同一类对象。到工厂里获取需要的功能,无需关注创建过程。
  2. 实现
    点菜程序如下:
// 饭店方法
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') // Error 这个菜本店没有

策略模式

  1. 定义:定义一系列的算法,把它们一个个封装起来,并且使其可以相互替换
  2. 核心
    至少由两部分组成:
    第一个部分是一组策略类,封装了具体的算法,负责具体的计算过程;
    第二个部分是环境类,接收客户的请求,然后把请求委托给某一个策略类
  3. 实现
    (1) 通过绩点等级来计算学生的最终得分
    (2) 表单提交校验
  4. 总结
    优点: 可以有效地避免多重条件语句,将一系列方法封装起来有利于理解和维护
    缺点: 往往策略集会比较多,需要事先了解定义好所有的情况

代理模式

  1. 定义:为对象提供一种代理,以控制对它的访问
  2. 实现(主要有三种)
    保护代理(无值时拒绝访问主体)
    虚拟代理(访问主体前进行一些额外的操作,比如说图片懒加载)
    缓存代理(为一些开销大的运算结果提供暂时的缓存,提升效率)

发布订阅者模式

  1. 定义:也称作观察者模式,定义了对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
       发布者->事件中心<=>订阅者。订阅者向事件中心订阅指定的事件->发布者向事件中心发布指定事件内容->事件中心通知订阅者,订阅者收到消息(可能有多个订阅者)。
  2. 实现:微博;vue框架;EventBus实现;promise实例
// 原生js实现EventBus
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('未注册该事件')
    }
    // 如果没指定移除的handler,则移除整个eventName
    if (!handler) {
      Reflect.deleteProperty(this.handlerBus, eventName) // 等同于delete this.handlerBus[eventName]
      return
    }
    // 如果指定了handler
    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)
// 移除event1的f1方法
bus.$remove('event1', f1)
// 触发
bus.$emit('event1', { a: 1 }) // f2-->{ a: 1 }
  1. 总结
    优点:
     (1) 时间上的解耦:注册的订阅行为由消息的发布方来决定何时调用,订阅者不用持续关注,发布者会负责通知;
     (2) 对象上的解耦 :发布者不用提前知道消息的接受者是谁,发布者只需要遍历处理所有订阅该消息类型的订阅者发送消息即可,因此解耦了发布者和订阅者之间的联系
    缺点:
     (1) 创建订阅者会消耗一定的时间和内存,订阅的处理函数不一定会被执行,驻留内存有性能开销
     (2) 弱化了对象之间的联系,复杂情况下可能会导致程序难以理解和跟踪维护

观察者模式

  1. 定义:目标<=>观察者。观察者观察目标(监听目标)->目标发生变化,主动通知观察者。
    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当目标对象状态发生变化时,会通知所有观察者对象,并使其自动更新。
  2. 实现:可以用报纸期刊的订阅来形象地说明,有多少人订阅报纸,报社就会发多少份报纸,报社和订报纸的客户就是上述“一对多”的依赖关系。

二者区别

  1. 角色角度来看,发布订阅者模式需要三种角色,发布者、事件中心和订阅者;观察者模式需要两种角色,目标和观察者,无事件中心负责通信。
  2. 从耦合度上来看,发布订阅者是一个事件中心调度模式,订阅者和发布者是没有直接关联的,通过事件中心进行关联,两者是解耦的;而观察者模式中目标和观察者是直接关联的,耦合在一起(有些观念说观察者是解耦,解耦的是业务代码,不是目标和观察者本身)。

二者优缺点

  1. 发布订阅模式
    优点: 灵活,由于发布者和订阅者是解耦的,只要引入事件中心,无论在何处都可以发布订阅
    缺点 : a. 使用不当会造成数据流混乱,容易导致代码不好维护;b. 需要维护事件列队,订阅的事件越多,内存消耗越大
  2. 观察者模式
    优点: 响应式
    缺点: 不灵活

观察者模式是不是发布订阅模式

  • 网上关于这个问题的回答,出现了两极分化。其实我不知道发布订阅模式是不是观察者模式,就像我不知道辨别模式的关键是设计意图还是设计结构(理念),虽然《JavaScript设计模式与开发实践》一书中说了分辨模式的关键是意图而不是结构。
  • 如果以结构来分辨模式,发布订阅模式相比观察者模式多了一个中间件订阅器,所以发布订阅模式是不同于观察者模式的;如果以意图来分辨模式,他们都是实现了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新,那么他们就是同一种模式,发布订阅模式是在观察者模式的基础上做的优化升级。
  • 不过,不管他们是不是同一个设计模式,他们的实现方式确实有差别,在使用时应该根据场景来判断选择哪个。
  • 47
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值