【设计模式-行为型】观察者模式
一、什么是观察者模式
说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空门,最终加入义军,南征北战,一步步登上历史舞台。剧中对朱元璋的刻画非常细腻,展现了他从底层一步步走向权力巅峰的过程。
在剧中,朱元璋为了巩固皇权,设立了锦衣卫。这一情节反映了朱元璋对权力的绝对掌控和对潜在威胁的高度警惕。其中有一个电影情结,让我记忆深刻:
监视闲赋在家的刘伯温 -------情节还原
-
刘伯温罢官回乡:朱元璋在经历了杨宪事件后变得更加多疑,开始怀疑刘伯温及其他官员。为了加强对官员的监控,朱元璋密令二虎组建了一支秘密队伍,命名为“锦衣卫”,专门监视所有皇孙臣子。刘伯温因与朱元璋政见不合,被朱元璋拒见并逐渐被孤立。刘伯温意识到朱元璋可能随时会治他的罪,于是提前写好了遗嘱。
-
归乡途中被监视:刘伯温在归乡途中被锦衣卫检校吴风半路拦截,奉命护送他回青田老家。刘伯温这才断定朱元璋可能要对他不利。在护送过程中,吴风等人虽然表面上照顾刘伯温父子的起居,但实际上一直在监视他们的行动和对话。刘伯温等待着吴风的诛杀,但直到抵达青田老家,吴风也没有动手,这让刘伯温感到非常诧异。
-
后续发展:朱元璋不断赏赐刘伯温,试图通过这种方式让刘伯温回京。刘伯温最终决定回京,但吴风再次出现,奉命护送他返回京城。
这一情节生动地体现了观察者模式的核心逻辑:通过“观察者”(锦衣卫)监视“被观察对象”(刘伯温),并将情报汇报给“主题”(朱元璋)。这种模式不仅巩固了朱元璋的皇权,还通过动态监控和及时反应,确保了明朝初年的政治稳定。基于上面的例子,我们来解释一下什么是观察者模式:观察者模式是一种行为型设计模式,它通过定义对象之间的依赖关系,使得当一个对象(主题)的状态发生变化时,所有依赖于它的对象(观察者)都会自动得到通知并更新。这种模式非常适合用于“一对多”的依赖关系,其中一个对象的状态变化需要通知多个其他对象。
二、为什么用观察者模式
通过这个例子来说明一下为什么要使用观察者模式呢(朱元璋为啥使用锦衣卫)
-
解耦合:观察者模式使得主题和被观察对象之间松耦合,主题不需要直接与被观察对象互动,而是通过观察者获取信息。朱元璋(主题)不需要直接与刘伯温(被观察对象)互动,而是通过锦衣卫(观察者)来获取信息。这种间接的监控方式使得朱元璋和刘伯温之间保持了松耦合关系。朱元璋不需要了解刘伯温的具体行动细节,只需要通过锦衣卫获取关键信息,从而减少了直接干预带来的风险。
-
动态监控:观察者模式支持动态监控,主题可以实时获取被观察对象的状态变化,并及时做出反应。朱元璋需要实时掌握刘伯温的动态,以便在必要时采取行动。通过锦衣卫的监视,朱元璋可以在刘伯温有任何异常行为时迅速做出反应,确保皇权的稳固。
-
集中管理:观察者模式通过统一的接口管理多个观察者,使得主题能够集中管理所有观察者的行为。这提高了系统的整体协调性和一致性。朱元璋通过锦衣卫统一管理对刘伯温的监视,确保所有信息都能集中汇报到他这里。这种集中管理的方式使得朱元璋能够全面掌握局势,避免信息碎片化,从而更好地做出决策。
-
扩展性:观察者模式允许动态地添加或删除观察者,而不需要修改主题的代码。这使得系统在运行时可以根据需要灵活调整监控范围和方式。朱元璋还可以添加监控对象胡惟庸,后续剧情。
-
广播通信:观察者模式支持一对多的广播通信机制,当主题的状态发生变化时,所有观察者都会收到通知。这使得系统能够高效地传递信息,减少重复劳动。指令统一由朱元璋下达,广播给锦衣卫。
三、观察者模式示例
3.1 锦衣卫Demo
下面让我们来用代码还原一下场景:
- 定义锦衣卫行为(接收任务,反馈监听信息)
public interface Observer { void receiveTask(String taskDescription); // 接收监听任务 void reportBack(String report); // 向朱元璋反馈信息 }
- 定义主题类朱元璋的行为
import java.util.List; public interface Subject { void assignTask(String taskDescription); // 下达监听任务 void receiveReport(String report); // 接收反馈 }
- 定义具体主题类朱元璋
import java.util.ArrayList; import java.util.List; public class ZhuYuanZhang implements Subject { private List<Observer> observers = new ArrayList<>(); @Override public void assignTask(String taskDescription) { System.out.println("朱元璋下达监听任务:" + taskDescription); for (Observer observer : observers) { observer.receiveTask(taskDescription); } } @Override public void receiveReport(String report) { System.out.println("朱元璋收到反馈:" + report); } public void registerObserver(Observer observer) { observers.add(observer); } public void removeObserver(Observer observer) { observers.remove(observer); } }
- 定义具体实行监听示例锦衣卫(二虎小弟):锦衣卫作为观察者,负责监视刘伯温的行动,并向朱元璋汇报。
public class JinyiWei implements Observer { private String name; private Subject zhuYuanZhang; public JinyiWei(String name, Subject zhuYuanZhang) { this.name = name; this.zhuYuanZhang = zhuYuanZhang; } @Override public void receiveTask(String taskDescription) { System.out.println(name + " 接收到任务:" + taskDescription); // 模拟监听过程 String report = performMonitoring(taskDescription); // 向朱元璋反馈信息 reportBack(report); } @Override public void reportBack(String report) { zhuYuanZhang.receiveReport(report); } private String performMonitoring(String taskDescription) { // 模拟监听过程 return "监听结果:刘伯温 " + taskDescription; } }
- 开始监听刘伯温
public class Main { public static void main(String[] args) { // 创建朱元璋(主题) Subject zhuYuanZhang = new ZhuYuanZhang(); // 创建锦衣卫(观察者) Observer jinyiWei1 = new JinyiWei("锦衣卫 A", zhuYuanZhang); Observer jinyiWei2 = new JinyiWei("锦衣卫 B", zhuYuanZhang); // 注册锦衣卫 zhuYuanZhang.registerObserver(jinyiWei1); zhuYuanZhang.registerObserver(jinyiWei2); // 朱元璋下达监听任务 zhuYuanZhang.assignTask("监视刘伯温在青田老家的行动"); zhuYuanZhang.assignTask("监视刘伯温准备回京的行动"); // 移除一个锦衣卫 zhuYuanZhang.removeObserver(jinyiWei1); // 再次下达监听任务 zhuYuanZhang.assignTask("监视刘伯温抵达京城后的行动"); } } // 朱元璋下达监听任务:监视刘伯温在青田老家的行动 锦衣卫 A 接收到任务:监视刘伯温在青田老家的行动 锦衣卫 B 接收到任务:监视刘伯温在青田老家的行动 朱元璋收到反馈:监听结果:刘伯温 在青田老家与旧部密谈 朱元璋收到反馈:监听结果:刘伯温 在青田老家与旧部密谈 朱元璋下达监听任务:监视刘伯温准备回京的行动 锦衣卫 A 接收到任务:监视刘伯温准备回京的行动 锦衣卫 B 接收到任务:监视刘伯温准备回京的行动 朱元璋收到反馈:监听结果:刘伯温 准备回京,行动异常 朱元璋收到反馈:监听结果:刘伯温 准备回京,行动异常 朱元璋下达监听任务:监视刘伯温抵达京城后的行动 锦衣卫 B 接收到任务:监视刘伯温抵达京城后的行动 朱元璋收到反馈:监听结果:刘伯温 抵达京城后与胡惟庸密会
3.2 Spring 事件机制与消息队列(MQ)的观察者模式
在实际应用中,我们一般不自己实现观察者模式,多数使用到的是Spring 事件机制和消息队列(MQ)。
3.2.1 Spring 中观察者模式的四个角色
-
事件(Event):
-
定义:
ApplicationEvent
是所有事件对象的父类,继承自 JDK 的EventObject
。 -
作用:所有自定义事件都需要继承
ApplicationEvent
,并通过getSource()
方法获取事件源。 -
内置事件:Spring 提供了多种内置事件,如
ContextRefreshedEvent
、ContextStartedEvent
、ContextStoppedEvent
、ContextClosedEvent
和RequestHandledEvent
。
-
-
事件监听器(Listener):
-
定义:
ApplicationListener
是事件监听器接口,继承自 JDK 的EventListener
。 -
作用:监听器通过实现
onApplicationEvent(ApplicationEvent event)
方法来处理事件。当事件发生时,Spring 会调用此方法。 -
实现方式:可以通过实现
ApplicationListener
接口或使用@EventListener
注解来定义监听器。
-
-
事件源(Event Source):
-
定义:
ApplicationContext
是 Spring 的核心容器,也是事件的发布者。 -
作用:
ApplicationContext
继承自ApplicationEventPublisher
,通过publishEvent(Object event)
方法发布事件。 -
发布方式:事件可以由任何组件通过调用
ApplicationContext
的publishEvent
方法发布。
-
-
事件管理器(Event Multicaster):
-
定义:
ApplicationEventMulticaster
是事件管理器,负责事件监听器的注册和事件的广播。 -
作用:当
ApplicationContext
发布事件时,ApplicationEventMulticaster
负责将事件广播给所有注册的监听器。 -
注册方式:监听器可以通过注解(如
@EventListener
)或通过实现ApplicationListener
接口并注册到ApplicationContext
中。
-
3.2.2 Spring事件与MQ的对比
场景 | Spring 事件机制 | 消息队列(MQ) |
---|---|---|
单体应用内部事件传递 | ✅ 适合,低延迟 | ❌ 过于复杂,性能未必优于直接调用 |
分布式系统通信 | ❌ 需额外实现跨容器事件传递 | ✅ 天然支持分布式,适合跨系统通信 |
高可靠性场景 | ❌ 容器故障可能导致事件丢失 | ✅ 提供持久化和重试机制,确保消息不丢失 |
大规模并发消息处理 | ❌ 不支持高并发场景 | ✅ 专为高并发设计,支持海量消息传递 |
事务一致性要求高的场景 | ❌ 无原生事务支持,需手动处理 | ✅ 提供事务机制(如 Kafka 事务 API) |
消息顺序严格要求的场景 | ❌ 无顺序性保障 | ✅ 支持消息顺序(如 Kafka 分区内消息有序) |
原文地址:https://blog.csdn.net/qq_36038282/article/details/145279071
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!