个人博客: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 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() {
@Override
public void onEvent(String event) {
Log.d("TAG", "收到事件: " + event);
}
});
//在 Activity 生命周期回调中触发通知
manager.onPause()
3. 特点
1. 核心思想
发布订阅模式通过引入事件通道(Event Channel) 解耦发布者(publish) 与订阅者(Subscriber) 。发布者无需知道谁订阅了事件,订阅者也无需知道事件来源,二者仅通过事件类型标识符来通信。
角色定义
- Publisher(发布者): 触发事件,向通道发送消息。
- Subscriber(订阅者): 向通道注册回调,监听特定事件。
- Event Channel(事件通道): 管理事件路由,负责消息传递。
2. 实现示例
//上面的activity生命周期的监听,改成发布订阅,该怎么写呢?
// 事件总线(核心发布订阅逻辑)
//定义事件通道(Event Bus)
public class EventBus {
private static EventBus instance;
private Map> eventListeners = new HashMap<>();
// 单例模式
public static synchronized EventBus getInstance() {
if (instance == null) {
instance = new EventBus();
}
return instance;
}
// 订阅事件
public void subscribe(String eventType, EventListener listener) {
List 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 listeners = eventListeners.get(eventType);
if (listeners != null) {
listeners.remove(listener);
}
}
// 发布事件
public void publish(String eventType, String eventData) {
List 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 {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 发布 onCreate 事件
EventBus.getInstance().publish("activity_create", "MainActivity created");
}
@Override
protected void onPause() {
super.onPause();
// 发布 onPause 事件
EventBus.getInstance().publish("activity_pause", "MainActivity paused");
}
@Override
protected 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);
}
@Override
public 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. 特点
有人就是表示不服,说,他们实际上触发就是list的foreach循环,触发回调。只不过观察者模式是直接触发,发布订阅者是间接触发。欸,对,你说的没错。这就是区别。一个是直接触发,一个是间接触发。判断一个代码的好与坏,就一个标准。高类聚,低耦合。 观察者模式的直接触发,就是被观察者直接获取到观察者的引用,这就是高耦合。后面修改就比较麻烦,改动一点点,影响比较大。发布订阅者,这方面就比观察者好。
不要杠,杠那就是你说的都对。
维度 | 观察者模式 | 发布订阅模式 |
---|---|---|
耦合性 | 强耦合(Subject 直接管理 Observer) | 低耦合(通过事件通道解耦) |
通信方式 | 同步调用(如直接方法调用) | 可同步或异步(支持消息队列) |
事件类型支持 | 需手动扩展接口或参数区分 | 天然支持多事件类型 |
性能与复杂度 | 轻量级,适合简单场景 | 需事件通道,适合复杂系统 |
典型应用 | 数据绑定 | 全局事件总线、分布式消息系统 |
// 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();
}
});
}
// 注册全局事件总线
Vue.prototype.$eventBus = new Vue();
// 组件A:发布事件
this.$eventBus.$emit('data-updated', { data: 123 });
// 组件B:订阅事件
this.$eventBus.$on('data-updated', (data) => {
console.log('收到数据:', data);
});
观察者模式与发布订阅模式代表了两种不同的解耦哲学:
这又让我想到了,类似于TCP与UDP通信的区别。就这样吧,讲完了。实践是检验真理的唯一标准。光看可能云里雾里的,实际场景中用到了,就知道了。
两者在代码实现上有相似之处(遍历列表触发回调)。但它们的核心区别不在于数据结构(List 或 Map),而在于设计思想和应用场景。