揭秘观察者模式:如何打造实时响应、灵活扩展的软件架构

一、技术背景与应用场景

观察者模式,也被称为订阅-发布模式,在现代软件开发中占据着核心地位,广泛应用于商品系统、物流系统、监控系统、运营数据分析系统等众多领域。在事件驱动架构的设计中,观察者模式更是扮演了关键角色。当对象的状态改变时,这一变化被视为一个事件,观察者通过处理这些事件来执行相应的操作。

使用观察者模式的原因主要有两方面:

  1. 实时响应状态变化:该模式有助于快速响应被观察对象的属性或行为变化。例如,在商品配送过程中,包裹状态会经历配送中、接收中、退货中等多个阶段,每一个状态变更都可能触发一系列关联操作。观察者模式能够有效地将状态变更通知给物流监控、性能监控和运营系统等需了解变更的对象,确保及时应对并做出相应处理。

  2. 提升代码可扩展性:传统的耦合式设计中,若要使某个对象知道另一个对象的状态变化,往往需要直接在被观察对象的逻辑中插入通知调用。这种做法增加了代码耦合度,并使得新增观察者时必须修改原有代码,降低了系统的扩展性。相反,观察者模式允许观察者通过注册自身到被观察者的观察者列表中,无需修改被观察者代码即可实现灵活扩展。

典型应用场景包括但不限于

  • 商品库存量发生变化时,自动更新商品详情页及购物车信息。
  • 微信公众号文章推送,发布者无需关心具体订阅用户,只需发出通知。
  • 创建链式触发机制,如A对象的行为引发B对象的响应,进而影响C对象……
  • 社交媒体平台如微博或微信朋友圈,用户发布动态后,关注者会接收到通知。
  • 基于事件驱动编程,如Java UI框架中的键盘和鼠标事件,由监听器对象统一处理。

二、观察者模式定义与结构

观察者模式的核心是建立了一种一对多的依赖关系,使得当一个对象(被观察者)状态变化时,所有依赖它的对象(观察者)都能得到自动通知和更新。

观察者模式包含四个关键组成部分:

在这里插入图片描述

  1. 被观察者(Publisher/Subject):即发布者,是观察者关注的对象集合,如GitLab上的Git库,当其内容发生变更时,相关操作随之启动。
  2. 具体被观察者(PublisherImpl/SubjectImpl):实现了被观察者接口的方法,负责维护观察者列表以及在状态变化时通知观察者。
  3. 观察者(Observer):对被观察者状态感兴趣的对象,被存储在被观察者的注册列表中,以便在被通知时执行特定操作。
  4. 具体观察者(ObserverImpl):实现观察者接口的具体类,对接收到的通知进行实际的业务处理。

三、使用步骤举例

以账单监控和消息订阅为例说明观察者模式的使用:

  1. 账单监控示例

    • 定义了一个Publisher接口,其中包含添加和删除观察者方法以及通知余额变动的方法。

      public interface Publisher {
      
          void addObserver(Observer o);
          void removeObserver(Observer o);
          void notify(double amt);
      }
      
    • 具体实现类PublisherImpl维持了一个观察者列表,当账户余额减少且出现透支时,遍历并通知所有观察者。

      public class PublisherImpl implements Publisher{
      
          private String acct;
          private double balance;
          private List<Observer> myObservers;
      
          public PublisherImpl(String anAcct, double aBalance) {
              acct = anAcct;
              balance = aBalance;
              myObservers = new ArrayList();
          }
      
          @Override
          public void addObserver(Observer o) {
              myObservers.add(o);
          }
      
          @Override
          public void removeObserver(Observer o) {
              myObservers.remove(o);
          }
      
          @Override
          public void notify(double amt) {
              balance -= amt;
              if(balance < 0) {
                  overdrawn();
              }
          }
      
          private void overdrawn() {
              for (Observer o: myObservers) {
                  o.notify(acct, balance);
              }
          }
      
      
      }
      
    • Observer接口定义了处理账单通知的方法

      public interface Observer {
      
          void notify(String acct, double amt);
      }
      
    • ObserverImpl实现了这个方法,打印出具体的账单信息。

      public class ObserverImpl implements Observer{
          @Override
          public void notify(String acct, double amt) {
              System.out.println("====== 接收到通知:账户: " + acct + " 账单:" + amt);
          }
      }
      
    • 测试代码

      public class Demo {
         public static void main(String[] args) {
             PublisherImpl account = new PublisherImpl("test123", 10);
             ObserverImpl bill = new ObserverImpl();
             account.addObserver(bill);
             account.notify(11);
         }
      }
      
    • 测试结果

      在这里插入图片描述

  2. 消息订阅示例

    • 定义了MessageObserver接口,其中有一个update方法用于接收消息通知。

      public interface MessageObserver {
         void update(Message m);
      }
      
    • 设计了一个Subject接口,包含增加和删除观察者以及发送消息更新的方法。

      public interface Subject {
         void attach(MessageObserver o);  //增加观察者
         void detach(MessageObserver o);  //删除观察者
         void notifyUpdate(Message m);    //更新通知
      }
      
    • 消息对象

      public class Message {
          final String content;
          public Message (String m) {
              this.content = m;
          }
          public String getContent() {
              return content;
          }
      }
      
    • 实现了MessagePublisher作为具体的消息发布者,持有观察者列表并在有新消息时遍历通知所有观察者。

      public class MessagePublisher implements Subject {
         private List<MessageObserver> observers = new ArrayList<>();
         @Override
         public void attach(MessageObserver o) {
             observers.add(o);
         }
         @Override
         public void detach(MessageObserver o) {
             observers.remove(o);
         }
         @Override
         public void notifyUpdate(Message m) {
             observers.forEach(x->x.update(m));
         }
      }
      
    • 创建了三个不同的MessageSubscriber实现类,它们各自实现了消息接收到后的处理逻辑。

      public class MessageSubscriber1 implements MessageObserver {
         @Override
         public void update(Message m) {
             System.out.println("MessageSubscriber1 :: " + m.getContent());
         }
      }
      public class MessageSubscriber2 implements MessageObserver {
         @Override
         public void update(Message m) {
             System.out.println("MessageSubscriber2 :: " + m.getContent());
         }
      }
      public class MessageSubscriber3 implements MessageObserver {
         @Override
         public void update(Message m) {
             System.out.println("MessageSubscriber3 :: " + m.getContent());
         }
      }
      
    • 测试代码

    public class Client {
        public static void main(String[] args) {
            MessageObserver s1 = new MessageSubscriber1();
            MessageObserver s2 = new MessageSubscriber2();
            MessageObserver s3 = new MessageSubscriber3();
            Subject p = new MessagePublisher();
            p.attach(s1);//
            p.attach(s2);
            p.notifyUpdate(new Message("First Message"));   //s1和s2会收到消息通知
            p.detach(s1);
            p.attach(s3);
            p.notifyUpdate(new Message("Second Message")); //s2和s3会收到消息通知
        }
    }
    
    • 测试结果

      在这里插入图片描述

四、优缺点分析

观察者模式的优点在于:

  • 降低耦合性:它促进了基于事件驱动的松散耦合系统构建,使得各个模块独立性强,易于扩展。
  • 增强代码扩展性:抽象的观察者与被观察者关系让系统能轻易地添加或移除观察者,遵循开闭原则,符合里氏替换原则,提高了代码的扩展性。
  • 建立触发机制:通过特定事件触发观察者的一系列操作,适用于消息通知系统、性能监控系统等多种场景。

然而,观察者模式也存在一些挑战:

  • 复杂性增加:由于观察者和被观察者之间是组合而非继承关系,理解程序逻辑时需要理清各组件之间的交互和依赖。
  • 性能开销:随着观察者数量的增长,通知所有观察者所需的时间也会相应增加,可能影响整体系统效率。

总结

尽管观察者模式在理论上具有一定的抽象性,但通过不同应用场景下的实践和变体,开发者已经将其转化为易于理解和使用的模式。在观察者模式中,被观察者通常维护一个观察者列表,当状态变化时便通知列表中的观察者执行相应的动作。

现实生活中,诸如微信公众号订阅的例子完美体现了观察者模式的工作原理。因此,在使用观察者模式时,关键在于识别哪些状态变化是至关重要的,既要避免过度捕获无关的变化,也要确保不会遗漏重要状态的转换。只有这样,才能充分利用观察者模式的优势,高效构建适应需求变化的应用系统。

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值