设计模式
设计模式
观察者模式
定义: 观察者模式(Observer Pattern)是一种软件设计模式,也被称作模型-视图(Model-View)模式、源-收听者(Listener)模式或从属者模式。它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会主动通知所有观察者对象,而观察者对象会收到通知并自动更新。
结构:
- 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题(Concrete Subject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- 具体观察者(Concrete Observer):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
优点:
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
缺点:
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
发布订阅者模式
定义: 发布订阅者模式(Publish-Subscribe Pattern)是一种消息传递模式,用于解耦消息发送者和接收者之间的依赖关系。订阅者将自己想订阅的事件注册到调度中心(Event Channel),当发布者发布该事件到调度中心时,由调度中心统一调度订阅者注册到调度中心的处理代码。
特征:
- 是一对多的关系(一个调度中心对应多个订阅者)。
- 通常会有一个对应消息队列的概念,用于存储订阅者的回调函数。
优点:
- 双向解耦,发布者和订阅者都不用清楚对方的存在,全部由调度中心做处理。
- 程序便于扩展,可以动态地增加或减少订阅者。
缺点:
- 如果调度中心出现故障,可能会导致消息传递失败。
- 订阅者需要处理来自不同发布者的消息,可能需要更复杂的逻辑来区分和处理。
发布订阅者模式应用
JavaScript 的事件回调机制
JavaScript 的事件回调机制在概念上更接近于发布订阅者模式(Publish-Subscribe Pattern),尽管在实际应用中它可能结合了观察者模式的一些特性。
发布订阅者模式涉及三个主要部分:发布者(Publisher)、订阅者(Subscriber)和消息代理(Message Broker,也称为事件通道或调度中心)。在这个模式中,发布者不直接与订阅者通信,而是通过消息代理来转发消息。订阅者会向消息代理注册它们感兴趣的消息类型(或称为事件),并在消息到来时接收通知。
在 JavaScript 的事件系统中,DOM 元素可以视为消息的发布者,而事件监听器(event listeners)则是订阅者。事件对象(event object)作为消息在它们之间传递。然而,与典型的发布订阅者模式不同,JavaScript 的 DOM 事件系统并没有一个显式的“消息代理”对象,但我们可以将浏览器的事件处理机制视为这个隐式的消息代理。
当某个 DOM 元素(发布者)上发生事件(如点击、滚动等)时,浏览器会创建一个事件对象,并将该事件对象传递给所有注册在该元素上并指定了相应事件类型(如'click'、'scroll'等)的事件监听器(订阅者)。这些事件监听器随后会接收到事件对象作为参数,并执行相应的回调函数。
虽然这个过程没有显式地创建一个消息代理对象,但它确实遵循了发布订阅者模式的核心原则:发布者(DOM 元素)和订阅者(事件监听器)之间的解耦,以及通过某种机制(浏览器的事件处理机制)来传递消息(事件对象)。
因此,可以说 JavaScript 的事件回调机制在概念上更接近于发布订阅者模式。不过,由于 JavaScript 的灵活性和多样性,它也可以被用来实现观察者模式或其他设计模式。
观察者 VS 发布订阅者
相同点:
- 两者都用于处理对象间的依赖关系,实现一对多的通信机制。
- 当某个对象(发布者/主题)的状态发生变化时,都会通知所有依赖的对象(订阅者/观察者)。
不同点:
- 耦合度:在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录;而在发布订阅模式中,发布者和订阅者不知道对方的存在,它们通过消息代理(调度中心)进行通信,实现了更高的解耦。
- 结构复杂度:观察者模式结构相对简单,直接在观察者和主题之间进行通信;而发布订阅者模式引入了调度中心的概念,增加了系统的复杂度,但也提高了系统的灵活性和可扩展性。
- 应用场景:观察者模式常用于实现MVC(Model-View-Controller)架构中的Model和View之间的通信;而发布订阅者模式则更适用于需要高度解耦和动态扩展的系统,如消息队列、事件驱动架构等。
观察者模式和发布订阅者模式应用
Vue中的响应式原理
在Vue中的响应式原理中,既使用了观察者模式也使用了发布订阅者模式,但更偏向于两者的结合使用。
具体来说,Vue通过以下方式实现响应式系统:
- 观察者模式:在Vue中,每个组件实例都可以视为一个观察者(Observer),而Vue实例中的数据对象(data)则可以被视为被观察者(Subject)。当数据对象的状态发生变化时,所有依赖于这些数据的组件(观察者)都会收到通知并自动更新。这种机制符合观察者模式的基本思想。
- 发布订阅者模式:Vue内部实现了一个依赖收集系统,这个系统可以看作是发布订阅者模式的一个应用。在这个系统中,Vue会为每个响应式的数据对象创建一个依赖收集器(Dep),用于收集所有依赖于该数据的组件或指令(订阅者)。当数据发生变化时,Vue会发布一个更新事件,并通知所有订阅了该数据的组件或指令进行更新。这种机制通过引入一个中间层(依赖收集器)来解耦了数据对象和订阅者之间的直接联系,从而实现了发布订阅者模式。
在Vue 2.x中,Vue主要使用了Object.defineProperty
方法来劫持数据的getter和setter,从而在数据被访问和修改时触发依赖收集和更新通知。而在Vue 3.x中,Vue则使用了ES6的Proxy
对象来替代Object.defineProperty
,从而实现了更细粒度的数据劫持和更高效的依赖收集。不过,无论是Vue 2.x还是Vue 3.x,它们都是通过结合使用观察者模式和发布订阅者模式来实现数据的响应式更新的。