当前位置: 首页 > news >正文

观察者模式与发布订阅模式:解耦与通信的艺术

个人博客:haichenyi.com。感谢关注

一. 目录

  • 一–目录
  • 二–引言
  • 三–观察者模式:直接依赖的同步通信​​
  • 四–发布订阅模式:解耦的异步通信​
  • 五–核心区别与对比​​
  • 六–实际应用场景​​​
  • 七–如何选择?​​​
  • 八–总结​​​​

二. 引言

  在软件开发中,组件间的通信是核心挑战之一。如何让对象在状态变化时通知其他对象,同时保持代码的灵活性和可维护性?观察者模式(Observer Pattern)和发布订阅模式(Publish-Subscribe Pattern)为此提供了经典解决方案。尽管两者目标相似(实现松耦合通信),但其设计哲学和应用场景却截然不同。本文将通过理论解析、代码示例和实际应用场景,深入探讨它们的区别与选择策略。
  举个案例,登录监听,有三个页面需要监听页面的变化,登录成功之后,通知页面更新

interface LoginObserver {loginUpdate(isLogin: boolean)
}class PageALoginOberver implements LoginObserver {loginUpdate(isLogin: boolean) {console.log("PageALoginOberver");}
}class PageBLoginOberver implements LoginObserver {loginUpdate(isLogin: boolean) {console.log("PageBLoginOberver");}
}class PageCLoginOberver implements LoginObserver {loginUpdate(isLogin: boolean) {console.log("PageCLoginOberver");}
}class LoginSubject {observerList: LoginObserver[]constructor() {this.observerList = []}addObserver(observer: LoginObserver) {if (!observer || this.observerList.indexOf(observer) !== -1) {return}this.observerList.push(observer)}removeObserver(observer: LoginObserver) {if (!observer) {return}let index = this.observerList.indexOf(observer)if (index !== -1) {this.observerList.splice(index, 1);}}notify(isLogin: boolean) {this.observerList.forEach(item => item.loginUpdate(isLogin))}
}let loginSubject = new LoginSubject()let pageALoginOberver = new PageALoginOberver()
let pageBLoginOberver = new PageBLoginOberver()
let pageCLoginOberver = new PageCLoginOberver()
loginSubject.addObserver(pageALoginOberver);
loginSubject.addObserver(pageBLoginOberver);
loginSubject.addObserver(pageCLoginOberver);//在登录成功的位置调用
loginSubject.notify(true)

这个案例属于哪种模式呢?

三. 观察者模式:直接依赖的同步通信

1. 核心思想​
  观察者模式定义了一种一对多的依赖关系: 当一个对象(被观察者Subject)的状态发生变化时,所有依赖它的对象(观察者Observer)会自动收到通知并更新
角色定义
- Subject(被观察者): ​维护观察者列表,提供注册、注销和通知方法。
- Observer(观察者)​​: 定义接收通知的接口(如 update() 方法)。
2. 实现示例

//上面引言中提到的案例,就是典型的观察者模式
//这里再举一个其他的案例,Android里面用的比较多的,Activity的生命周期的监听
// Subject(被观察者)
public class ActivityLifecycleManager {private List<LifecycleListener> listeners = new ArrayList<>();public void addListener(LifecycleListener listener) {listeners.add(listener);}public void removeListener(LifecycleListener listener) {listeners.remove(listener);}private void notifyListeners(String event) {for (LifecycleListener listener : listeners) {listener.onEvent(event); // 直接调用 Observer 的方法}}// 在 Activity 生命周期回调中触发通知public void onPause() {notifyListeners("onPause");}
}// Observer(观察者)
public interface LifecycleListener {void onEvent(String event);
}// 使用示例
ActivityLifecycleManager manager = new ActivityLifecycleManager();
manager.addListener(new LifecycleListener() {@Overridepublic void onEvent(String event) {Log.d("TAG", "收到事件: " + event);}
});
//在 Activity 生命周期回调中触发通知
manager.onPause()

3. 特点

  • 强耦合​​:Subject 直接持有 Observer 的引用。
  • 同步通信​​:通知是即时且阻塞的(如直接调用 Observer 的方法)。
  • 适用场景​​:对象间直接依赖的简单通信。(如数据模型更新)

四. 发布订阅模式:解耦的异步通信

1. 核心思想
  发布订阅模式通过引入事件通道(Event Channel) 解耦发布者(publish)订阅者(Subscriber) 。发布者无需知道谁订阅了事件,订阅者也无需知道事件来源,二者仅通过事件类型标识符来通信。
角色定义
- Publisher(发布者)​​: 触发事件,向通道发送消息。
- Subscriber(订阅者)​​: 向通道注册回调,监听特定事件。
- Event Channel(事件通道)​​: ​管理事件路由,负责消息传递。
2. 实现示例

//上面的activity生命周期的监听,改成发布订阅,该怎么写呢?
// 事件总线(核心发布订阅逻辑)
//定义事件通道(Event Bus)​
public class EventBus {private static EventBus instance;private Map<String, List<EventListener>> eventListeners = new HashMap<>();// 单例模式public static synchronized EventBus getInstance() {if (instance == null) {instance = new EventBus();}return instance;}// 订阅事件public void subscribe(String eventType, EventListener listener) {List<EventListener> listeners = eventListeners.get(eventType);if (listeners == null) {listeners = new ArrayList<>();eventListeners.put(eventType, listeners);}listeners.add(listener);}// 取消订阅public void unsubscribe(String eventType, EventListener listener) {List<EventListener> listeners = eventListeners.get(eventType);if (listeners != null) {listeners.remove(listener);}}// 发布事件public void publish(String eventType, String eventData) {List<EventListener> listeners = eventListeners.get(eventType);if (listeners != null) {for (EventListener listener : listeners) {listener.onEvent(eventData);}}}// 事件监听接口public interface EventListener {void onEvent(String eventData);}
}
//发布者(Activity 生命周期触发事件)​
//在 Activity 生命周期回调中发布事件,而不是直接调用监听器。
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 发布 onCreate 事件EventBus.getInstance().publish("activity_create", "MainActivity created");}@Overrideprotected void onPause() {super.onPause();// 发布 onPause 事件EventBus.getInstance().publish("activity_pause", "MainActivity paused");}@Overrideprotected void onDestroy() {super.onDestroy();// 发布 onDestroy 事件EventBus.getInstance().publish("activity_destroy", "MainActivity destroyed");}
}
//订阅者(监听生命周期事件)
//在任何地方订阅感兴趣的事件类型(如 activity_pause)。
public class MySubscriber implements EventBus.EventListener {public MySubscriber() {// 订阅 activity_pause 事件EventBus.getInstance().subscribe("activity_pause", this);}@Overridepublic void onEvent(String eventData) {Log.d("PubSub", "收到事件: " + eventData);// 处理事件逻辑(如保存数据、更新 UI)}// 取消订阅(避免内存泄漏)public void dispose() {EventBus.getInstance().unsubscribe("activity_pause", this);}
}// 使用示例
MySubscriber subscriber = new MySubscriber();

  有大聪明就会说了,我原来几行代码就写完了,你现在搞这么多代码,这么复杂。的确,上面activity生命周期,改成发布订阅者模式代码确实变多了。任何,设计的选型,都是需要结合实际情况和考虑后续拓展的。比方说,这里的生命周期的监听,你如果只监听了一种生命周期,那的确用观察者模式会简单很多。但是,activity的生命周期可不止一个啊。要监听多个生命后期,你如果用观察者模式,你需要加方法,加监听等等之类的。但是,你如果用发布订阅者模式,你啥都不用改,你只用改事件类型就可以了。这也是发布订阅者的其中一个优点:支持多对多通信,动态添加事件类型。
3. 特点

  • 完全解耦​​: 发布者和订阅者通过事件通道间接通信。
  • 灵活扩展​​: 支持多对多通信,动态添加事件类型。
  • 适用场景​​: 跨组件通信、微服务架构、分布式系统(如 Kafka、RabbitMQ)。

  有人就是表示不服,说,他们实际上触发就是list的foreach循环,触发回调。只不过观察者模式是直接触发,发布订阅者是间接触发。欸,对,你说的没错。这就是区别。一个是直接触发,一个是间接触发。判断一个代码的好与坏,就一个标准。高类聚,低耦合。 观察者模式的直接触发,就是被观察者直接获取到观察者的引用,这就是高耦合。后面修改就比较麻烦,改动一点点,影响比较大。发布订阅者,这方面就比观察者好。

  不要杠,杠那就是你说的都对。

五. 核心区别与对比

维度观察者模式发布订阅模式
耦合性​强耦合(Subject 直接管理 Observer)低耦合(通过事件通道解耦)
​​通信方式​​同步调用(如直接方法调用)可同步或异步(支持消息队列)
事件类型支持​​需手动扩展接口或参数区分天然支持多事件类型
性能与复杂度​​轻量级,适合简单场景需事件通道,适合复杂系统
典型应用​​数据绑定全局事件总线、分布式消息系统

六. 实际应用场景

  1. 观察者模式的应用​
  • 前端框架的响应式系统
    Vue 2 通过 Object.defineProperty 劫持数据属性的 getter/setter,结合 Dep 和 Watcher 实现数据变化到视图的同步。
// Vue 2 响应式原理简化实现
class Dep {constructor() { this.subs = []; }depend() { this.subs.push(Dep.target); }notify() { this.subs.forEach(watcher => watcher.update()); }
}function defineReactive(obj, key, val) {const dep = new Dep();Object.defineProperty(obj, key, {get() {if (Dep.target) dep.depend();return val;},set(newVal) {val = newVal;dep.notify();}});
}
  • Android 生命周期监听​
    Activity 直接管理生命周期监听器列表,状态变化时遍历触发回调。
  1. 发布订阅模式的应用​
  • ​Vue 的全局事件总线
// 注册全局事件总线
Vue.prototype.$eventBus = new Vue();// 组件A:发布事件
this.$eventBus.$emit('data-updated', { data: 123 });// 组件B:订阅事件
this.$eventBus.$on('data-updated', (data) => {console.log('收到数据:', data);
});
  • Android里面的EventBus库
  • 微服务架构中的消息中间件​
    使用 Kafka 或 RabbitMQ 实现服务间的异步通信,确保系统可扩展性和容错性。

七. 如何选择?

  1. 选择观察者模式的条件​
    • 对象间存在直接依赖关系。
    • 事件类型较少,且不需要动态扩展。
    • 需要简单、轻量级的同步通信。
  2. 选择发布订阅模式的条件​
    • 需要跨组件或跨服务通信。
    • 事件类型多样,且可能动态增减。
    • 系统需要解耦以提升可维护性和扩展性。

八. 总结​

观察者模式与发布订阅模式代表了两种不同的解耦哲学:

  • ​​观察者模式​​ 是 “我直接通知你”,强调直接、高效的同步通信。
  • 发布订阅模式​​ 是 “我发到频道,谁爱听谁听”,强调灵活性和解耦。

这又让我想到了,类似于TCP与UDP通信的区别。就这样吧,讲完了。实践是检验真理的唯一标准。光看可能云里雾里的,实际场景中用到了,就知道了。

两者在代码实现上有相似之处(遍历列表触发回调)。​​但它们的核心区别不在于数据结构(List 或 Map),而在于设计思想和应用场景​​。

相关文章:

  • SpringBoot 动态加载 Jar 包
  • 【c语言】深入理解指针2
  • Python 获取淘宝券后价接口的详细指南
  • 2025年机动车检测站授权签字人考试真题及答案
  • 【C++】map和set
  • Windows11-24h2的任务栏时间显示秒 笔记250417
  • 更强的视觉 AI!更智能的多模态助手!Qwen2.5-VL-32B-Instruct-AWQ 来袭
  • 【OSG学习笔记】Day 3: 加载你的第一个3D模型
  • Pytest 的钩子函数 (Hook Functions):定制你的测试流程 (Pytest 系列之五)
  • 关于webpack的知识点
  • 67. 二进制求和
  • MongoServerError: Authentication failed.处理办法
  • 利用 Java 爬虫按关键字搜索淘宝商品
  • ServletContextListener 的用法笔记250417
  • 第十七届“华中杯”B 题校园共享单车的调度与维护问题分析
  • 第九章:强化学习(RL)赋能 AI Agents:潜力、挑战与问题建模
  • 【MCP】第一篇:MCP协议深度解析——大模型时代的“神经连接层“架构揭秘
  • 极狐GitLab 安全文件管理功能介绍
  • C++纯虚函数
  • 05-libVLC的视频播放器:设置倍速播放
  • 剑指3000亿产业规模,机器人“武林大会”背后的无锡“野望”
  • 招商蛇口:一季度营收约204亿元,净利润约4.45亿元
  • 东风着陆场做好各项搜救准备,迎接神舟十九号航天员天外归来
  • 青海省林业和草原局副局长旦增主动投案,正接受审查调查
  • 阿里开源首个“混合推理模型”:集成“快思考”、“慢思考”能力
  • 新一届中国女排亮相,奥运冠军龚翔宇担任队长