前提
观察者模式是我们在开发时经常会用到的模式,最近在维护公司项目时看见了前辈的代码用到了观察者模式。就想来和大家讲解观察者模式
观察者模式
生搬硬套概念肯定会让我们新手很难理解,刚好最近华为手机出新机了,一机难抢,我们就拿这个事情来做案例吧。
自从华为的新手机发布后,就遭到了大家的疯狂抢购,导致供不应求。你问我为什么?那当然是国产芯片遥遥领先的功劳。
因为很多人想买,但是手机库存不足,所以我们的某宝就推出了订阅模式,当我们的用户添加订阅后,一但有新的手机到货发售,就会通知我们添加订阅的用户。但是要通过什么方式通知呢?当然是需要用户留下自己的手机号码,然后平台才能通过用户的手机号码发送短信通知用户。
那么在用户订阅的功能中有两类主体
- 某宝(售卖平台)
- 点击了添加订阅的用户名单列表
- 订阅了的用户名单列表添加新的订阅用户的功能
- 通知订阅名单上的用户的功能
- 当已订阅用户,取消订阅的功能
- 想要买华为手机的用户
- 自己的手机号码
那你可能会问这和观察者模式有什么关系?
那关系可大了,某宝和用户之间的订阅通知关系,就是用到了观察模式的设计思路。
你可能还会问那什么是观察者模式呢?
当对象之间存在一对多的依赖关系时,其中一个对象的状态发生改变,所有依赖它的对象都会收到通知,这就是观察者模式。
在观察者模式中,只有两种主体:目标对象 (Object) 和 观察者 (Observer)。华为手机就是目标对象,想买手机的用户就是观察者
-
目标对象
Subject
:- 维护观察者列表
observerList
------------ 点击了添加订阅的用户名单列表 - 定义添加观察者的方法 ------------ 订阅了的用户名单列表添加新的订阅用户的功能
- 当自身发生变化后,通过调用自己的
notify
方法依次通知每个观察者执行update
方法 ------------ 通知订阅名单上的用户的功能
- 维护观察者列表
-
观察者
Observer
:- 需要实现
update
方法,供目标对象调用。 update
方法中可以执行自定义的业务逻辑- 用户收到通知后去平台购买手机。
- 需要实现
定义
当你使用观察者模式时,你可以创建一个对象(称为主题或发布者),并允许其他对象(称为观察者或订阅者)订阅该主题。当主题状态发生变化时,所有订阅该主题的观察者都会收到通知。
javascript
// 主题(Subject)或发布者对象(某宝平台)
class TaobaoStore {
constructor() {
this.observers = []; // 存储订阅的用户名单
}
// 当有新的用户点击了添加订阅,添加到我们的订阅用户名单
addObserver(observer) {
this.observers.push(observer);
}
// 当已经买到手机的用户,点击取消订阅时,从订阅的用户名单中移除
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
// 通过用户自己的手机号码给所有的用户发信息,通知所有订阅了的用户
notifyObservers() {
this.observers.forEach(observer => {
observer.update(this);
});
}
// 当有新的手机到货时,通知所有用户
someStateChanged() {
// 执行状态变化后的操作...
// 然后通知观察者
this.notifyObservers();
}
}
// 观察者(Observer)(想要买手机的用户)
class Observer {
constructor(name) {
this.name = name;
}
// 接收到通知时,去购买手机或执行相应操作
update(subject) {
console.log(`${this.name} 收到到货通知,可以去购买手机了`);
// 在这里可以执行一些相应的操作,比如跳转到购买页面等
}
}
// 使用示例
const taobao = new TaobaoStore();
const user1 = new Observer('用户A');
const user2 = new Observer('用户B');
taobao.addObserver(user1);
taobao.addObserver(user2);
// 模拟新手机到货通知
taobao.someStateChanged();
// 输出:
// 用户A 收到到货通知,可以去购买手机了
// 用户B 收到到货通知,可以去购买手机了
// 移除用户A的订阅
taobao.removeObserver(user1);
// 再次模拟新手机到货通知
taobao.someStateChanged();
// 输出:
// 用户B 收到到货通知,可以去购买手机了
图解结构
这是一个基本的观察者模式的实现示例。主题对象(Subject)负责管理观察者列表,并在状态发生变化时通知观察者。观察者对象(Observer)订阅主题,并在接收到通知时执行相应的操作。
优点和缺点
优点:
- 松散耦合:观察者模式允许主题(被观察者)和观察者(订阅者)之间保持松散耦合,主题并不需要知道观察者的具体细节,只需要知道观察者实现了特定的接口或方法即可。这使得系统中的对象之间的交互更灵活、可扩展性更强。
- 发布-订阅模式:观察者模式类似于发布-订阅模式,它允许多个观察者订阅同一个主题,并在主题状态发生变化时接收通知。这种模式有助于在分布式系统中实现消息传递和事件驱动的架构。
- 可扩展性:由于松散耦合的特性,可以随时增加或移除观察者,而不需要对主题对象进行修改。这样做对系统的可扩展性有很大帮助,可以根据需要动态添加新的观察者。
- 模块化设计:观察者模式支持模块化设计,每个观察者可以专注于自己的任务,主题对象负责管理状态变化并通知观察者,从而使系统更易于维护和管理。
缺点:
- 可能引起性能问题:如果通知过于频繁,观察者模式可能导致性能问题。当主题对象有大量观察者时,在状态变化时需要通知所有观察者,可能会影响系统性能。可以通过合理设计来减少频繁通知的问题。
- 可能引起循环依赖:观察者模式可能导致循环依赖的问题。如果观察者之间相互依赖,可能会导致循环调用,增加调试和维护的难度。需小心设计避免这种情况发生。
- 可能引起内存泄漏:如果观察者没有被正确地移除或处理,可能会导致内存泄漏问题。当观察者长时间存在,而不再需要时,如果没有正确地解除订阅关系,可能会造成内存泄漏。