设计模式——观察者设计模式(行为型)

摘要

本文详细介绍了观察者设计模式,包括其定义、结构、实现方式、适用场景以及实战示例。通过代码示例展示了如何在Spring框架下实现观察者模式,以及如何通过该模式实现状态变化通知。同时,对比了观察者模式与消息中间件在设计理念、耦合程度、通信方式和分布式支持等方面的差异,帮助读者更好地理解和选择合适的实现方式。

1. 观察者设计模式定义

观察者设计模式(Observer Pattern)是一种行为型设计模式,定义了一种一对多的依赖关系,使得当一个对象(被观察者)的状态发生改变时,所有依赖它的对象(观察者)都会得到自动通知并更新,从而实现对象间的松耦合和实时通讯。观察者模式通过定义对象间的发布-订阅关系,实现事件的自动通知和响应,适合事件驱动和异步消息场景。

1.1. 核心定义

角色

  • 被观察者(Subject):维护一组观察者,负责状态的管理和通知。
  • 观察者(Observer):注册到被观察者,一旦被观察者状态变化,接收通知并执行相应动作。

目的:让多个观察者对象自动获得被观察者的状态变化,降低对象间耦合度。

2. 观察者设计模式结构

观察者模式包含如下角色:

  • Subject: 目标
  • ConcreteSubject: 具体目标
  • Observer: 观察者
  • ConcreteObserver: 具体观察者

2.1. 观察者模式类图

2.2. 观察者模式时序图

3. 观察者设计模式实现方式

3.1. 观察者设计模式实现步骤

3.1.1. 定义抽象主题(Subject)接口

  • 提供注册、移除和通知观察者的方法。

    public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
    }

3.1.2. 定义抽象观察者(Observer)接口

  • 声明一个更新方法,主题状态变化时调用。

    public interface Observer {
    void update(String message);
    }

3.1.3. 具体主题类实现 Subject

  • 维护观察者列表,实现注册、移除、通知逻辑。

  • 当自身状态变化时调用 notifyObservers(),遍历通知所有观察者。

    import java.util.ArrayList;
    import java.util.List;

    public class ConcreteSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private String state;

    复制代码
      @Override
      public void registerObserver(Observer observer) {
          observers.add(observer);
      }
    
      @Override
      public void removeObserver(Observer observer) {
          observers.remove(observer);
      }
    
      @Override
      public void notifyObservers() {
          for (Observer observer : observers) {
              observer.update(state);
          }
      }
    
      public void setState(String state) {
          this.state = state;
          notifyObservers();
      }

    }

3.1.4. 具体观察者实现 Observer

  • 实现 update 方法,接收通知并做出响应。

    public class ConcreteObserver implements Observer {

    复制代码
      private String name;
    
      public ConcreteObserver(String name) {
          this.name = name;
      }
    
      @Override
      public void update(String message) {
          System.out.println(name + " 收到通知,状态变为: " + message);
      }

    }

3.2. 观察者测试示例

复制代码
public class ObserverPatternDemo {
    
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observer1 = new ConcreteObserver("观察者1");
        Observer observer2 = new ConcreteObserver("观察者2");

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        subject.setState("状态1");
        subject.setState("状态2");
    }
}

说明

  • 主题(Subject)维护观察者集合,状态变化时通知所有观察者。
  • 观察者实现更新接口,获得主题最新状态。

4. 观察者设计模式适合场景

4.1. ✅ 适合使用观察者设计模式的场景

|-------------|--------------------------------------|
| 适合场景 | 说明 |
| 事件驱动系统 | 事件产生后需要通知多个模块或对象做出响应,如 UI 事件监听、消息推送。 |
| 一对多依赖关系 | 一个对象状态变化需要通知多个依赖对象,如股票价格更新通知所有订阅者。 |
| 松耦合需求 | 希望对象间解耦,观察者与被观察者不直接依赖,实现灵活扩展。 |
| 广播通信 | 需要将消息广播给多个接收者,且接收者可动态增加和移除。 |
| 异步通知 | 状态更新后,通知观察者进行异步处理。 |
| 分布式系统 | 多个服务或模块之间的状态同步与消息推送。 |

4.2. ❌ 不适合使用观察者设计模式的场景

|-----------------|----------------------------|
| 不适合场景 | 说明 |
| 对象之间不存在依赖关系 | 彼此独立的对象无需相互通知。 |
| 通知对象数量固定且简单 | 只有少数固定对象,且耦合关系明确,使用简单调用即可。 |
| 需要严格同步控制的场景 | 观察者通知是异步的,无法满足严格同步时序需求。 |
| 性能敏感场景 | 大量观察者通知导致性能开销大,影响系统响应速度。 |
| 过度使用导致复杂性增加 | 观察者链条过长,维护困难,代码难以理解。 |

总结:观察者模式主要适用于需要"自动广播状态变化给多个对象"且"希望对象间低耦合"的场景。不适合简单调用、同步严格或性能瓶颈明显的场景。

5. 观察者设计模式实战示例

5.1. 场景说明

在风控系统中,当订单状态发生变化(比如风控审核结果出来后),需要通知多个模块(比如日志记录模块、报警模块、缓存更新模块)来响应该状态变化。

5.2. 定义观察者接口

复制代码
public interface OrderStatusObserver {
    void update(String orderId, String status);
}

5.3. 定义具体观察者实现类

复制代码
import org.springframework.stereotype.Component;

@Component
public class LoggingObserver implements OrderStatusObserver {

    @Override
    public void update(String orderId, String status) {
        System.out.println("日志模块:订单 " + orderId + " 状态更新为:" + status);
        // 这里可以写日志持久化操作
    }
}

@Component
public class AlertObserver implements OrderStatusObserver {

    @Override
    public void update(String orderId, String status) {
        if ("REJECTED".equals(status)) {
            System.out.println("报警模块:订单 " + orderId + " 审核拒绝,触发报警!");
            // 触发报警逻辑
        }
    }
}

@Component
public class CacheObserver implements OrderStatusObserver {

    @Override
    public void update(String orderId, String status) {
        System.out.println("缓存模块:更新订单 " + orderId + " 状态缓存为:" + status);
        // 缓存刷新逻辑
    }
}

5.4. 定义主题接口和实现类

复制代码
public interface OrderStatusSubject {
    void registerObserver(OrderStatusObserver observer);
    void removeObserver(OrderStatusObserver observer);
    void notifyObservers(String orderId, String status);
}

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

@Component
public class OrderStatusSubjectImpl implements OrderStatusSubject {

    private final List<OrderStatusObserver> observers = new ArrayList<>();

    // 使用Spring自动注入所有实现了OrderStatusObserver接口的Bean
    @Autowired
    private List<OrderStatusObserver> observerBeans;

    @PostConstruct
    public void init() {
        // 初始化时,将所有观察者注册进列表
        observers.addAll(observerBeans);
    }

    @Override
    public void registerObserver(OrderStatusObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(OrderStatusObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers(String orderId, String status) {
        for (OrderStatusObserver observer : observers) {
            observer.update(orderId, status);
        }
    }
}

5.5. 4. 业务调用示例(比如风控服务)

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RiskControlService {

    @Autowired
    private OrderStatusSubject orderStatusSubject;

    public void auditOrder(String orderId, boolean passed) {
        // 风控业务逻辑
        String status = passed ? "APPROVED" : "REJECTED";
        System.out.println("风控审核完成,订单:" + orderId + ",状态:" + status);

        // 审核完成后,通知所有观察者
        orderStatusSubject.notifyObservers(orderId, status);
    }
}

5.6. 测试Controller(可选)

复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RiskController {

    @Autowired
    private RiskControlService riskControlService;

    @PostMapping("/auditOrder")
    public String auditOrder(@RequestParam String orderId, @RequestParam boolean passed) {
        riskControlService.auditOrder(orderId, passed);
        return "审核完成";
    }
}

5.7. 实战示例总结

  • 观察者接口 OrderStatusObserver,多个实现类由Spring管理并注入。
  • 主题 OrderStatusSubjectImpl 维护观察者列表,利用 @Autowired 自动注入所有观察者Bean。
  • 风控业务执行后调用主题的 notifyObservers(),实现状态变化通知。
  • 全部采用Spring注解管理,未用构造函数注入,代码简洁且易于扩展。

6. 观察者设计模式思考

6.1. 观察着模式和使用消息中间件来通知处理的方式有什么不同?

很好的问题!观察者模式和消息中间件在实现"发布-订阅"或通知机制时都很常用,但它们的设计理念、实现方式和适用场景有显著差别。下面帮你详细分析两者的区别:

|------------|--------------------------------------------|----------------------------------|
| 维度 | 观察者模式 | 消息中间件(如Kafka、RabbitMQ、ActiveMQ等) |
| 设计层次 | 设计模式,属于应用内部的设计范式,通常是在代码内部直接实现。 | 独立的中间件系统,属于基础设施层,提供异步消息传递机制。 |
| 耦合程度 | 被观察者和观察者耦合较紧密,观察者必须知道被观察者实例(尽管解耦设计可以降低耦合)。 | 发布者和订阅者完全解耦,彼此不直接依赖,只通过消息中间件通信。 |
| 通信方式 | 通常是同步调用,状态变化时立即调用观察者的更新方法。 | 典型异步通信,消息发送后,接收方异步消费,支持异步处理和缓冲。 |
| 分布式支持 | 一般用于单体应用或同一JVM内对象之间的通知。 | 天生支持分布式系统,跨进程、跨服务器,甚至跨数据中心的消息传递。 |
| 扩展性 | 观察者数量有限,扩展时需要修改代码或依赖注入配置。 | 高度扩展,支持大量消费者和复杂路由规则,方便动态添加消费者。 |
| 容错与持久化 | 无持久化和容错机制,一旦通知失败可能丢失信息。 | 支持消息持久化、重试、消息确认、死信队列,增强系统可靠性。 |
| 性能影响 | 观察者调用是同步的,影响调用链的执行时间,可能导致性能瓶颈。 | 异步处理,解耦主业务流程,提升系统吞吐量和响应速度。 |
| 使用场景 | 小型系统、UI事件响应、简单状态同步、模块内部事件通知。 | 大型分布式系统、微服务间异步通信、异步任务调度、事件驱动架构。 |

6.1.1. 简单总结:

  • 观察者模式 适合应用内、轻量级的同步通知,设计简单,适合对象之间状态变化通知。
  • 消息中间件 适合跨进程、跨网络、异步且高可靠的消息通信,适合分布式系统异步解耦和复杂事件流转。

6.1.2. 举例对比:

  • 观察者模式:风控模块风控结果出来后,同步通知日志模块和报警模块,立即处理。
  • 消息中间件:风控模块发布"风控结果"消息到消息队列,日志模块和报警模块异步订阅该消息,消息队列保证消息可靠送达和解耦。

博文参考

相关推荐
suixinger_lmh12 小时前
功能结构整理
unity·设计模式·c#·源代码管理
冰茶_15 小时前
建造者模式:优雅构建复杂对象
设计模式·微软·c#·.netcore·建造者模式·软件开发
码农秋15 小时前
设计模式系列(07):建造者模式(Builder)
设计模式·建造者模式
庄小焱16 小时前
设计模式——访问者设计模式(行为型)
设计模式
master-dragon16 小时前
设计模式-观察者模式
java·观察者模式·设计模式
冰茶_16 小时前
结构型设计模式之桥接模式
学习·设计模式·微软·c#·.netcore·桥接模式
何中应16 小时前
【设计模式-3.5】结构型——装饰器模式
java·设计模式·装饰器模式
哆啦A梦的口袋呀18 小时前
基于Python学习《Head First设计模式》 第一章 策略模式
python·学习·设计模式
冰茶_1 天前
适配器模式:让不兼容接口协同工作
microsoft·设计模式·适配器模式
magic 2451 天前
Java设计模式详解:策略模式(Strategy Pattern)
java·设计模式·策略模式