自学内容网 自学内容网

【设计模式-行为型】观察者模式

一、什么是观察者模式

        说起观察者模式,不得不说一位观察者模式的高级应用者,朱元璋。不知道大家有没有看过胡军演的电视剧《朱元璋》。这部剧背景是元朝末年,天下大乱,朱元璋自幼父母双亡,沦为乞丐,后遁入空门,最终加入义军,南征北战,一步步登上历史舞台。剧中对朱元璋的刻画非常细腻,展现了他从底层一步步走向权力巅峰的过程。

        在剧中,朱元璋为了巩固皇权,设立了锦衣卫。这一情节反映了朱元璋对权力的绝对掌控和对潜在威胁的高度警惕。其中有一个电影情结,让我记忆深刻:

监视闲赋在家的刘伯温 -------情节还原

  1. 刘伯温罢官回乡:朱元璋在经历了杨宪事件后变得更加多疑,开始怀疑刘伯温及其他官员。为了加强对官员的监控,朱元璋密令二虎组建了一支秘密队伍,命名为“锦衣卫”,专门监视所有皇孙臣子。刘伯温因与朱元璋政见不合,被朱元璋拒见并逐渐被孤立。刘伯温意识到朱元璋可能随时会治他的罪,于是提前写好了遗嘱。

  2. 归乡途中被监视:刘伯温在归乡途中被锦衣卫检校吴风半路拦截,奉命护送他回青田老家。刘伯温这才断定朱元璋可能要对他不利。在护送过程中,吴风等人虽然表面上照顾刘伯温父子的起居,但实际上一直在监视他们的行动和对话。刘伯温等待着吴风的诛杀,但直到抵达青田老家,吴风也没有动手,这让刘伯温感到非常诧异。

  3. 后续发展:朱元璋不断赏赐刘伯温,试图通过这种方式让刘伯温回京。刘伯温最终决定回京,但吴风再次出现,奉命护送他返回京城。

        这一情节生动地体现了观察者模式的核心逻辑:通过“观察者”(锦衣卫)监视“被观察对象”(刘伯温),并将情报汇报给“主题”(朱元璋)。这种模式不仅巩固了朱元璋的皇权,还通过动态监控和及时反应,确保了明朝初年的政治稳定。基于上面的例子,我们来解释一下什么是观察者模式:观察者模式是一种行为型设计模式,它通过定义对象之间的依赖关系,使得当一个对象(主题)的状态发生变化时,所有依赖于它的对象(观察者)都会自动得到通知并更新。这种模式非常适合用于“一对多”的依赖关系,其中一个对象的状态变化需要通知多个其他对象

二、为什么用观察者模式

        通过这个例子来说明一下为什么要使用观察者模式呢(朱元璋为啥使用锦衣卫)

  1. 解耦合:观察者模式使得主题和被观察对象之间松耦合,主题不需要直接与被观察对象互动,而是通过观察者获取信息。朱元璋(主题)不需要直接与刘伯温(被观察对象)互动,而是通过锦衣卫(观察者)来获取信息。这种间接的监控方式使得朱元璋和刘伯温之间保持了松耦合关系。朱元璋不需要了解刘伯温的具体行动细节,只需要通过锦衣卫获取关键信息,从而减少了直接干预带来的风险。

  2. 动态监控:观察者模式支持动态监控,主题可以实时获取被观察对象的状态变化,并及时做出反应。朱元璋需要实时掌握刘伯温的动态,以便在必要时采取行动。通过锦衣卫的监视,朱元璋可以在刘伯温有任何异常行为时迅速做出反应,确保皇权的稳固。

  3. 集中管理:观察者模式通过统一的接口管理多个观察者,使得主题能够集中管理所有观察者的行为。这提高了系统的整体协调性和一致性。朱元璋通过锦衣卫统一管理对刘伯温的监视,确保所有信息都能集中汇报到他这里。这种集中管理的方式使得朱元璋能够全面掌握局势,避免信息碎片化,从而更好地做出决策。

  4. 扩展性:观察者模式允许动态地添加或删除观察者,而不需要修改主题的代码。这使得系统在运行时可以根据需要灵活调整监控范围和方式。朱元璋还可以添加监控对象胡惟庸,后续剧情。

  5. 广播通信:观察者模式支持一对多的广播通信机制,当主题的状态发生变化时,所有观察者都会收到通知。这使得系统能够高效地传递信息,减少重复劳动。指令统一由朱元璋下达,广播给锦衣卫。

三、观察者模式示例

3.1 锦衣卫Demo

        下面让我们来用代码还原一下场景:

  1. 定义锦衣卫行为(接收任务,反馈监听信息)
    public interface Observer {
        void receiveTask(String taskDescription); // 接收监听任务
        void reportBack(String report); // 向朱元璋反馈信息
    }
  2. 定义主题类朱元璋的行为
    
    import java.util.List;
    
    public interface Subject {
        void assignTask(String taskDescription); // 下达监听任务
        void receiveReport(String report); // 接收反馈
    }
  3. 定义具体主题类朱元璋
    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);
        }
    }
  4. 定义具体实行监听示例锦衣卫(二虎小弟):锦衣卫作为观察者,负责监视刘伯温的行动,并向朱元璋汇报。
    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;
        }
    }
  5. 开始监听刘伯温
    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 中观察者模式的四个角色

  1. 事件(Event)

    • 定义ApplicationEvent 是所有事件对象的父类,继承自 JDK 的 EventObject

    • 作用:所有自定义事件都需要继承 ApplicationEvent,并通过 getSource() 方法获取事件源。

    • 内置事件:Spring 提供了多种内置事件,如 ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEventRequestHandledEvent

  2. 事件监听器(Listener)

    • 定义ApplicationListener 是事件监听器接口,继承自 JDK 的 EventListener

    • 作用:监听器通过实现 onApplicationEvent(ApplicationEvent event) 方法来处理事件。当事件发生时,Spring 会调用此方法。

    • 实现方式:可以通过实现 ApplicationListener 接口或使用 @EventListener 注解来定义监听器。

  3. 事件源(Event Source)

    • 定义ApplicationContext 是 Spring 的核心容器,也是事件的发布者。

    • 作用ApplicationContext 继承自 ApplicationEventPublisher,通过 publishEvent(Object event) 方法发布事件。

    • 发布方式:事件可以由任何组件通过调用 ApplicationContextpublishEvent 方法发布。

  4. 事件管理器(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)!