15、Java 观察者模式从入门到实战

Java 观察者模式从入门到实战(后端必看,附案例+面试考点)

前言:观察者模式(Observer Pattern)是 Java 行为型设计模式中的核心模式,也被叫做发布 - 订阅模式 ,完美解决了对象间一对多依赖、一个对象状态变更自动通知其他依赖对象的场景问题。

日常开发中,消息推送、事件监听、缓存更新、订单状态同步等场景,都离不开观察者模式;Spring 的事件驱动、RabbitMQ 消息队列、Guava EventBus 等技术,底层也都基于观察者模式思想实现。

本文从痛点问题、核心概念、入门代码、真实业务实战、框架应用、高频面试题全维度拆解,带你彻底吃透观察者模式,新手能直接上手,老手能落地项目,面试轻松拿高分。

一、为什么要学观察者模式?(痛点直击)

先看后端开发中高频遇到的问题,不用观察者模式,代码会变得极其臃肿、耦合严重:

  1. 订单支付成功后:需要同步更新订单状态、推送支付成功消息、增加用户积分、扣减商品库存,所有逻辑写在一个方法里,代码冗长,新增 / 删除逻辑需修改原有代码;

  2. 用户注册成功后:需要发送注册短信、初始化用户账户、记录注册日志,业务逻辑高度耦合,违背开闭原则;

  3. 状态变更通知:一个对象状态改变,多个对象需要感知并执行操作,硬编码调用导致代码难以维护、扩展性极差。

这些问题的核心:对象间强耦合,状态变更无法自动通知,业务逻辑耦合在一起,维护和扩展成本极高

观察者模式 ,就是专门解决这类一对多依赖、状态自动通知、业务解耦的问题,将发布者和订阅者完全分离,发布者只负责发布消息,订阅者只负责接收并处理消息,新增订阅逻辑无需修改发布者代码,完美符合开闭原则。

简单理解:观察者模式就像公众号订阅,公众号(被观察者 / 主题)发布文章,所有关注的用户(观察者)都会收到推送,用户可随时订阅 / 取消订阅,公众号无需关心有多少用户关注。

二、观察者模式核心概念与角色

2.1 模式定义

观察者模式:定义对象间的一对多依赖关系,当一个对象(被观察者 / 主题)的状态发生改变时,所有依赖它的对象(观察者)都会收到通知并自动更新。

2.2 核心角色(5 大核心,必记)

  1. 抽象主题(Subject):也叫抽象被观察者,定义添加、删除、通知观察者的接口,持有观察者集合;

  2. 具体主题(ConcreteSubject):实现抽象主题接口,维护自身状态,状态变更时通知所有观察者;

  3. 抽象观察者(Observer):定义收到通知后的更新方法,所有具体观察者必须实现;

  4. 具体观察者(ConcreteObserver):实现抽象观察者接口,接收到通知后执行业务逻辑;

  5. 客户端(Client):创建主题和观察者对象,将观察者注册到主题中,触发主题状态变更。

2.3 核心执行流程

  1. 客户端创建主题对象和多个观察者对象;

  2. 将观察者注册到主题的观察者列表中;

  3. 主题状态发生变更,遍历观察者列表,调用每个观察者的更新方法;

  4. 观察者接收通知,执行自身业务逻辑。

三、观察者模式入门实现(原生 Java,可直接复制)

订单支付通知为例,实现原生观察者模式,模拟支付成功后,消息服务、积分服务、库存服务自动执行逻辑。

3.1 抽象观察者(Observer)

java 复制代码
/**
 * 抽象观察者
 */
public interface OrderObserver {
    /**
     * 订单状态更新通知
     * @param orderId 订单ID
     */
    void update(String orderId);
}

3.2 具体观察者(多个)

java 复制代码
/**
 * 消息服务观察者:发送支付成功短信
 */
@Component
public class MessageObserver implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("【消息服务】订单:" + orderId + " 支付成功,已发送短信通知");
    }
}

/**
 * 积分服务观察者:增加用户积分
 */
@Component
public class IntegralObserver implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("【积分服务】订单:" + orderId + " 支付成功,已增加用户积分");
    }
}

/**
 * 库存服务观察者:扣减商品库存
 */
@Component
public class StockObserver implements OrderObserver {
    @Override
    public void update(String orderId) {
        System.out.println("【库存服务】订单:" + orderId + " 支付成功,已扣减商品库存");
    }
}

3.3 抽象主题(Subject)

java 复制代码
/**
 * 抽象主题(被观察者)
 */
public interface OrderSubject {
    // 添加观察者
    void addObserver(OrderObserver observer);
    // 删除观察者
    void removeObserver(OrderObserver observer);
    // 通知所有观察者
    void notifyObservers(String orderId);
}

3.4 具体主题

java 复制代码
/**
 * 订单主题:具体被观察者
 */
@Component
public class OrderConcreteSubject implements OrderSubject {

    // 观察者集合
    private List<OrderObserver> observerList<>();

    @Override
    public void addObserver(OrderObserver observer) {
        observerList.add(observer);
    }

    @Override
    public void removeObserver(OrderObserver observer) {
        observerList.remove(observer);
    }

    @Override
    public void notifyObservers(String orderId) {
        // 遍历通知所有观察者
        for (OrderObserver observer : observerList) {
            observer.update(orderId);
        }
    }
}

3.5 客户端测试

java 复制代码
public class ObserverTest {
    public static void main(String[] args) {
        // 1. 创建主题(被观察者)
        OrderSubject orderSubject = new OrderConcreteSubject();

        // 2. 创建观察者并注册到主题
        OrderObserver messageObserver = new MessageObserver();
        OrderObserver integralObserver = new IntegralObserver();
        OrderObserver stockObserver = new StockObserver();

        orderSubject.addObserver(messageObserver);
        orderSubject.addObserver(integralObserver);
        orderSubject.addObserver(stockObserver);

        // 3. 模拟订单支付成功,通知所有观察者
        System.out.println("订单:ORDER_20240520_001 支付成功");
        orderSubject.notifyObservers("ORDER_20240520_001");
    }
}

3.6 运行结果

Plain 复制代码
订单:ORDER_20240520_001 支付成功
【消息服务】订单:ORDER_20240520_001 支付成功,已发送短信通知
【积分服务】订单:ORDER_20240520_001 支付成功,已增加用户积分
【库存服务】订单:ORDER_20240520_001 支付成功,已扣减商品库存

3.7 代码优势

  1. 解耦彻底:发布者(订单主题)和订阅者(各服务)完全分离,互不依赖;

  2. 扩展性强:新增 / 删除业务逻辑,只需新增 / 删除观察者,无需修改主题代码;

  3. 职责单一:每个观察者只负责自身业务逻辑,符合单一职责原则。

四、Java 原生观察者模式(内置 API)

Java 本身提供了java\.util\.Observable(抽象主题)和java\.util\.Observer(抽象观察者)接口,可直接使用,无需自定义接口。

4.1 具体主题(继承 Observable)

java 复制代码
import java.util.Observable;

/**
 * Java原生订单主题
 */
public class JavaOrderSubject extends Observable {
    // 订单支付成功,触发通知
    public void paySuccess(String orderId) {
        // 标记状态已变更
        setChanged();
        // 通知所有观察者
        notifyObservers(orderId);
    }
}

4.2 具体观察者(实现 Observer)

java 复制代码
import java.util.Observable;
import java.util.Observer;

/**
 * 原生消息观察者
 */
public class JavaMessageObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        String orderId = (String) arg;
        System.out.println("【原生消息服务】订单:" + orderId + " 支付成功,已发送通知");
    }
}

注意:Java 内置的 Observable 是类,而非接口,存在单继承局限,实际开发中更推荐自定义接口实现。

五、真实业务实战(Spring Boot + 观察者模式)

实际后端开发中,结合 Spring Boot 实现观察者模式,通过依赖注入管理对象,适配真实业务场景,代码可直接复用。

5.1 实战场景

用户注册成功后,自动执行:

  1. 发送注册成功短信;

  2. 初始化用户个人账户;

  3. 记录用户注册日志。

5.2 核心代码实现

1. 抽象观察者
java 复制代码
public interface UserRegisterObserver {
    void handleRegisterSuccess(Long userId);
}
2. 具体观察者
java 复制代码
// 短信服务观察者
@Service
public class SmsObserver implements UserRegisterObserver {
    @Override
    public void handleRegisterSuccess(Long userId) {
        System.out.println("【短信服务】用户ID:" + userId + " 注册成功,发送短信通知");
    }
}

// 账户初始化观察者
@Service
public class AccountObserver implements UserRegisterObserver {
    @Override
    public void handleRegisterSuccess(Long userId) {
        System.out.println("【账户服务】用户ID:" + userId + " 注册成功,初始化个人账户");
    }
}

// 日志记录观察者
@Service
public class LogObserver implements UserRegisterObserver {
    @Override
    public void handleRegisterSuccess(Long userId) {
        System.out.println("【日志服务】用户ID:" + userId + " 注册成功,记录注册日志");
    }
}
3. 主题(被观察者)
java 复制代码
@Service
public class UserRegisterSubject {

    @<UserRegisterObserver> registerObservers;

    // 用户注册成功,通知所有观察者
    public void registerSuccess(Long userId) {
        System.out.println("用户ID:" + userId + " 注册成功");
        for (UserRegisterObserver observer : registerObservers) {
            observer.handleRegisterSuccess(userId);
        }
    }
}
4. 控制层调用
java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserRegisterSubject userRegisterSubject;

    @PostMapping("/register")
    public String register(Long userId) {
        // 模拟用户注册逻辑
        userRegisterSubject.registerSuccess(userId);
        return "注册成功";
    }
}

5.3 实战亮点

  1. 完全适配 Spring Boot 开发,通过 @Autowired 自动注入所有观察者;

  2. 无硬编码,新增业务只需新增观察者类,无需修改原有代码;

  3. 业务解耦,便于后续维护和扩展。

六、观察者模式在主流框架中的应用

6.1 Spring 事件驱动模型

Spring 的ApplicationEvent(事件)、ApplicationListener(事件监听),就是观察者模式的经典实现:

  • ApplicationEvent:抽象主题;

  • ApplicationListener:抽象观察者;

  • ApplicationContext:事件发布者,负责通知所有监听器。

6.2 Guava EventBus

Google Guava 提供的 EventBus,是观察者模式的优雅实现,简化了事件注册和通知,无需手动维护观察者列表。

6.3 RabbitMQ/RocketMQ 消息队列

消息队列的发布 - 订阅模式,本质是分布式的观察者模式,生产者(发布者)发送消息,消费者(订阅者)监听并消费消息,实现跨服务的解耦通知。

6.4 Swing/AWT 事件监听

Java 桌面开发中的按钮点击、鼠标移动等事件,都是基于观察者模式实现,组件是被观察者,事件监听器是观察者。

七、观察者模式高频面试考点(必背)

1. 观察者模式和发布 - 订阅模式的区别?

经典面试题,必背

  • 观察者模式:一对多,直接耦合,主题和观察者相互感知,在同一进程内;

  • 发布 - 订阅模式:多对多,完全解耦,通过中间件(消息队列、事件总线)通信,发布者和订阅者互不感知,可跨进程 / 跨服务。

2. 观察者模式的优缺点?

优点

  • 解耦:发布者和订阅者分离,互不依赖;

  • 可扩展:新增订阅逻辑无需修改原有代码;

  • 自动通知:状态变更自动通知所有依赖对象。

缺点

  • 观察者过多时,通知效率降低;

  • 观察者之间无依赖,无法感知相互执行顺序;

  • 若观察者和主题循环依赖,可能导致系统崩溃。

3. 观察者模式和责任链模式的区别?

  • 观察者模式:一对多,同时通知所有观察者

  • 责任链模式:一对一,依次传递请求,一个请求被一个处理器处理。

4. 什么场景适合用观察者模式?

  • 一个对象状态变更,需要通知其他多个对象;

  • 业务解耦,拆分耦合的业务逻辑;

  • 事件监听、消息推送、状态同步场景。

八、总结

观察者模式是后端开发必掌握的解耦神器 ,核心思想就是发布 - 订阅,彻底解决对象间一对多的依赖耦合问题,让代码更易维护、更易扩展。

新手学习从原生代码入手,理解核心角色和执行流程;实战开发结合 Spring Boot、EventBus 等工具,落地真实业务;面试重点掌握和发布 - 订阅模式的区别、优缺点、适用场景。

实际开发中,小场景用自定义观察者模式,大场景 / 分布式场景用消息队列,灵活选择,让代码更优雅、更健壮。

相关推荐
珹洺2 小时前
Java-Spring入门指南(二十三)俩万字超详细讲解利用IDEA手把手教你实现SSM(Spring + SpringMVC + MyBatis)整合,并构建第一个SSM基础系统
java·spring·intellij-idea
yaaakaaang2 小时前
十九、观察者模式
java·观察者模式
2401_835956812 小时前
如何利用SQL子查询进行实时监控数据分析_性能优化
jvm·数据库·python
小碗羊肉2 小时前
【从零开始学Java | 第三十八篇】序列化流(Object Stream)
java·开发语言
亚历克斯神2 小时前
Java 23 虚拟线程进阶:深度探索与实战
java·spring·微服务
百锦再2 小时前
使用JavaScript获取和解析页面内容的完整指南
开发语言·前端·javascript·python·flask·fastapi
想带你从多云到转晴2 小时前
02、JAVAEE--多线程(二)
java·开发语言·javaee
AIoT科技物语2 小时前
免费开源!50+算法,Java基于YOLO框架的视频AI识别算法平台,适配低空无人机巡检、摄像头安防场景
java·人工智能·算法·yolo·开源
黑客大白2 小时前
IDEA安装教程配置java环境(超详细)_idea配置java,零基础入门到精通,收藏这篇就够了
java·ide·intellij-idea