黑马程序员技术交流社区

标题: 【上海校区】观者者模式与发布订阅模式的区别 [打印本页]

作者: 透露姓名的网民    时间: 2020-3-11 17:35
标题: 【上海校区】观者者模式与发布订阅模式的区别
观察者模式和订阅-发布模式的区别?
观察者模式中主体和观察者是互相感知的,发布-订阅模式是借助第三方来实现调度的,发布者和订阅者之间互不感知
在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。
在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。
观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。
观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

观察者模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
注意:有些人认为观察者模式就是发布订阅模式,但实际上观察者模式和发布订阅模式是有区别的。
区别:观察者模式只有两个,一个是观察者一个是被观察者。发布订阅模式不一样,发布订阅模式还有一个中间层,发布订阅模式的实现是,发布者通知给中间层 => 中层接受并通知订阅者 => 订阅者收到通知并发生变化
我们曾经在DOM节点上面绑定过事件函数,那我们就使用过观察者模式,因为JS和DOM之间就是实现了一种观察者模式。
// 创建对象
var targetObj = {
    name:'小李'
}
var targetObj2 = {
    name:'小李'
}
// 定义值改变时的处理函数(观察者)
function observer(oldVal, newVal) {
    // 其他处理逻辑...
    targetObj2.name = newVal
    console.info('targetObj2的name属性的值改变为 ' + newVal);
}

// 定义name属性及其set和get方法(name属性为被观察者)
Object.defineProperty(targetObj, 'name', {
    enumerable: true,
    configurable: true,
    get: function() {
        return name;
    },
    set: function(val) {
        //调用处理函数
        observer(name, val)
        name = val
    }
});

targetObj.name = '张三';
targetObj.name = '李四';
console.log(targetObj2.name)
虽然我们只是改变了targetObj的name属性,但是因为观察者模式的设计,targetObj2的name属性同样被改变,这就实现了一个简单的观察者模式。


发布-订阅模式里面包含了三个模块,发布者,订阅者和处理中心。
其实就是将发布者和订阅者解耦了,在实际开发中,经常会遇到某个方法内处理很多的逻辑,最简单的就是直接在方法内直接写。这种是高度耦合的面向过程的写法。对于代码维护不友好。而发布-订阅模式就是将两者分离。我触发了某个事件(这里我们将触发该方法定义为事件),我只向调度中心通知,我并不知道调度中心内会怎么处理,有多少个人响应。我只管通知。而订阅者只管在调度中心订阅,有人调用它才响应。
还有一点就是假设我们有3个js文件,事件触发在a.js内,而响应该事件的在b.js和c.js内,要是用常规调用的方法的话,就要把b.js和c.js的方法传到a.js内。这是一个非常麻烦的操作。而发布-订阅模式是将调度中心挂在了全局,我们只管调用调度中心相应的方法注册和订阅。
class Event {
  constructor () {}
  // 首先定义一个事件容器,用来装事件数组(因为订阅者可以是多个)
  handlers = {}

  // 事件添加方法,参数有事件名和事件方法
  addEventListener (type, handler) {
    // 首先判断handlers内有没有type事件容器,没有则创建一个新数组容器
    if (!(type in this.handlers)) {
      this.handlers[type] = []
    }
    // 将事件存入
    this.handlers[type].push(handler)
  }

  // 触发事件两个参数(事件名,参数)
  dispatchEvent (type, ...params) {
    // 若没有注册该事件则抛出错误
    if (!(type in this.handlers)) {
      return new Error('未注册该事件')
    }
    // 便利触发
    this.handlers[type].forEach(handler => {
      handler(...params)
    })
  }

  // 事件移除参数(事件名,删除的事件,若无第二个参数则删除该事件的订阅和发布)
  removeEventListener (type, handler) {
      // 无效事件抛出
      if (!(type in this.handlers)) {
        return new Error('无效事件')
      }
      if (!handler) {
        // 直接移除事件
        delete this.handlers[type]
      } else {
        const idx = this.handlers[type].findIndex(ele => ele === handler)
        // 抛出异常事件
        if (idx === undefined) {
          return new Error('无该绑定事件')
        }
        // 移除事件
        this.handlers[type].splice(idx, 1)
        if (this.handlers[type].length === 0) {
          delete this.handlers[type]
        }
      }
    }
}

var event = new Event() // 创建event实例
// 定义一个自定义事件:"load"
function load (params) {
  console.log('load', params)
}
event.addEventListener('load', load)
// 再定义一个load事件
function load2 (params) {
  console.log('load2', params)
}
event.addEventListener('load', load2)
// 触发该事件
event.dispatchEvent('load', 'load事件触发')
// 移除load2事件
event.removeEventListener('load', load2)
// 移除所有load事件
event.removeEventListener('load')












作者: 梦缠绕的时候    时间: 2020-3-19 17:00
很好
作者: 放荡的人生    时间: 2020-4-9 17:07
够够的棒
作者: 小影姐姐    时间: 2020-4-9 17:14
加油同学,加油黑马!
作者: 晨大喵    时间: 2020-4-9 17:17
加油加油
作者: 小影姐姐    时间: 2020-4-9 17:17
努力!奋斗!
作者: xiaowang66    时间: 2020-4-9 17:52

作者: 放荡的人生    时间: 2020-4-23 17:13
加油




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2