观察者模式Java实现与设计模式实例代码详解
作者:伊斯特本
简介:观察者模式是一种常用的行为型设计模式,定义了一对多的依赖关系,当被观察对象状态变化时,所有观察者会收到通知并自动更新。本文通过Java语言,详细讲解如何使用 ApplicationListener 和 ApplicationEvent 实现观察者模式,并提供完整的测试流程,包括可观察对象创建、观察者注册、事件发布与响应机制。适用于GUI交互、数据模型更新、消息通知等场景,帮助开发者构建高内聚、低耦合的系统架构。
1. 观察者模式简介与应用场景
观察者模式是一种经典的行为型设计模式,核心用于实现对象间的一对多依赖关系。当一个对象(称为主题或Subject)的状态发生变化时,所有依赖于它的对象(称为观察者或Observer)都会自动收到通知并作出响应。该模式广泛应用于事件驱动系统中,如图形用户界面(GUI)的事件监听机制、数据模型与视图的同步更新、消息通知系统等。
其主要角色包括:
- Subject(主题) :维护观察者列表,提供注册与移除观察者的接口,并在状态变化时通知所有观察者。
- Observer(观察者) :定义一个更新接口,用于接收主题的通知并做出响应。
观察者模式的优势在于 解耦合 ,使得主题和观察者之间无需了解彼此的具体实现,只需通过接口进行通信。这种松耦合结构提升了系统的可维护性和可扩展性。
2. Java内置观察者接口介绍
Java 语言自早期版本起就提供了对观察者模式的支持,其核心在于 java.util.Observer 接口与 java.util.Observable 类的配合使用。本章将深入解析 Java 内置的观察者机制,包括其设计原理、使用流程以及实际开发中可能遇到的问题。同时,我们还将探讨其局限性,并为读者提供替代方案的建议。
2.1 Java中的观察者支持机制
2.1.1 Observable类与Observer接口的作用
Java 提供了两个核心组件用于实现观察者模式: Observable 类与 Observer 接口。
Observable类 :代表“被观察对象”(Subject),它维护了一个观察者列表,并在状态变化时通知这些观察者。Observer接口 :定义了观察者的行为,其中只有一个方法update(Observable o, Object arg),用于接收通知。
它们之间的关系可以总结为:
| 类型 | 名称 | 作用描述 |
|---|---|---|
| 被观察对象 | Observable | 维护观察者列表,负责通知 |
| 观察者 | Observer | 接收通知并做出响应 |
注意:
Observable是一个类,而非接口,因此它有一定的局限性。例如,如果某个类已经继承了其他类,就无法再继承Observable。
2.1.2 基于Java内置API实现观察者模式的基本流程
使用 Java 内置 API 实现观察者模式的基本流程如下:
- 创建可观察对象 :继承
Observable类,并定义状态变化的方法。 - 创建观察者 :实现
Observer接口,并重写update方法。 - 注册观察者 :通过
addObserver()方法将观察者注册到可观察对象。 - 触发通知 :当可观察对象的状态发生变化时,调用
setChanged()和notifyObservers()方法通知所有观察者。
下面是一个简单的示例:
import java.util.Observable;
import java.util.Observer;
// 1. 创建可观察对象
class WeatherData extends Observable {
private String weather;
public void setWeather(String weather) {
this.weather = weather;
setChanged(); // 标记状态已改变
notifyObservers(weather); // 通知所有观察者
}
}
// 2. 创建观察者
class WeatherDisplay implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg instanceof String) {
System.out.println("天气更新:" + arg);
}
}
}
public class Main {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
// 3. 注册观察者
WeatherDisplay display1 = new WeatherDisplay();
WeatherDisplay display2 = new WeatherDisplay();
weatherData.addObserver(display1);
weatherData.addObserver(display2);
// 4. 触发通知
weatherData.setWeather("晴天");
}
}代码逻辑分析
- 第1部分 :
WeatherData继承自Observable,并定义了一个setWeather方法用于改变状态并通知观察者。 setChanged():标记当前对象状态已改变,是调用notifyObservers()的前提。notifyObservers(weather):将参数weather传递给所有注册的观察者。- 第2部分 :
WeatherDisplay实现了Observer接口,并在update()方法中打印天气信息。 - 第3部分 :在
main()方法中创建了两个观察者实例,并通过addObserver()注册到weatherData。 - 第4部分 :调用
setWeather()方法触发通知,观察者会收到更新信息。
2.2 使用Java内置API进行观察者模式开发
2.2.1 定义可观察对象(Observable)
定义可观察对象的核心在于继承 Observable 类,并在其内部状态发生变化时调用通知方法。需要注意的是, Observable 类是“有状态”的,即它维护了一个内部的 changed 标志位,只有在该标志位为 true 时, notifyObservers() 才会真正触发通知。
class Product extends Observable {
private String name;
private double price;
public void setPrice(double price) {
if (this.price != price) {
this.price = price;
setChanged(); // 只有状态变化时才通知
notifyObservers(price);
}
}
}参数说明 :
-setChanged():将changed标志设为true。
-notifyObservers(Object arg):将参数arg传递给所有观察者的update()方法。
2.2.2 实现观察者(Observer)接口并注册
观察者需要实现 Observer 接口,并重写 update() 方法。注册时使用 addObserver() 方法。
class PriceMonitor implements Observer {
private String name;
public PriceMonitor(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
if (arg instanceof Double) {
System.out.println(name + " 收到价格更新:" + arg);
}
}
}参数说明 :
-o:被观察对象的引用。
-arg:传递的通知数据。
注册观察者:
Product product = new Product();
product.addObserver(new PriceMonitor("Monitor A"));
product.addObserver(new PriceMonitor("Monitor B"));2.2.3 触发事件与接收通知
当可观察对象的状态发生改变时,调用 setChanged() 和 notifyObservers() 即可触发事件通知。
product.setPrice(199.0);
输出示例:
Monitor A 收到价格更新:199.0 Monitor B 收到价格更新:199.0
事件通知流程图(Mermaid)
graph TD
A[Observable.setChanged()] --> B[notifyObservers()]
B --> C{观察者列表是否为空?}
C -->|否| D[调用每个Observer.update()]
C -->|是| E[无操作]
2.3 Java内置观察者API的局限性与替代方案
2.3.1 线程安全与性能问题分析
Java 内置的 Observable 类并不是线程安全的。当多个线程同时修改状态并触发通知时,可能会导致观察者列表的并发修改异常( ConcurrentModificationException )。
并发修改示例
Product product = new Product();
product.addObserver((o, arg) -> System.out.println("收到通知:" + arg));
// 多线程修改
new Thread(() -> product.setPrice(100.0)).start();
new Thread(() -> product.setPrice(200.0)).start();问题分析 :
-Observable内部使用Vector存储观察者,虽然Vector是线程安全的,但notifyObservers()在遍历过程中没有同步控制。
- 当多个线程同时调用setChanged()和notifyObservers()时,可能导致并发修改异常。
性能瓶颈
Observable类没有提供异步通知机制,所有通知都是同步执行的,容易导致主线程阻塞。- 不支持观察者优先级、事件过滤等高级功能。
2.3.2 推荐使用Spring框架的ApplicationListener机制
Spring 框架提供了一套更为强大和灵活的事件监听机制,基于 ApplicationEvent 和 ApplicationListener 接口,能够很好地替代 Java 原生的观察者机制。
Spring ApplicationListener 简单使用示例
@Component
class PriceChangeEvent extends ApplicationEvent {
public PriceChangeEvent(Object source) {
super(source);
}
}
@Component
class PriceChangeListener implements ApplicationListener<PriceChangeEvent> {
@Override
public void onApplicationEvent(PriceChangeEvent event) {
System.out.println("Spring监听到价格变化:" + event.getSource());
}
}
@Service
class ProductService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void updatePrice(double newPrice) {
System.out.println("价格更新为:" + newPrice);
eventPublisher.publishEvent(new PriceChangeEvent(newPrice));
}
}优势总结 :
- 支持异步监听(通过@Async)
- 支持多监听器与事件过滤
- 集成 Spring 上下文,便于管理生命周期
与 Java 内置机制对比(表格)
| 特性 | Java Observable | Spring ApplicationListener |
|---|---|---|
| 线程安全 | 否 | 是 |
| 异步支持 | 否 | 是 |
| 事件过滤 | 否 | 是 |
| 优先级设定 | 否 | 是 |
| 容器集成 | 否 | 是 |
使用建议
对于中大型项目或需要高并发处理的系统,建议优先使用 Spring 的事件机制,以获得更好的扩展性和维护性。而对于小型项目或快速原型开发,Java 原生的观察者机制仍可作为快速实现方案使用。
本章详细介绍了 Java 内置的观察者接口及其使用方式,通过代码示例展示了如何实现基本的观察者模式,并分析了其存在的局限性。在下一章中,我们将进一步探讨 Spring 框架中的事件监听机制,了解其更强大的功能与使用方式。
3. ApplicationListener实现原理与使用
Spring框架提供了强大的事件驱动机制,其中 ApplicationListener 是实现观察者模式的重要组件之一。通过 ApplicationEvent 和 ApplicationListener 的组合,Spring 实现了模块之间的松耦合通信,适用于系统中事件驱动逻辑的实现。本章将从事件监听机制的整体架构入手,深入讲解 ApplicationListener 的使用方式、高级特性以及底层实现原理,帮助开发者构建可扩展、可维护的 Spring 事件驱动应用。
3.1 Spring框架中的事件监听机制概述
Spring 的事件机制是基于观察者模式设计的,其核心接口包括 ApplicationEvent 和 ApplicationListener 。这种机制使得应用程序可以在不同模块之间实现事件的发布与监听,从而实现解耦和异步通信。
3.1.1 ApplicationEvent与ApplicationListener的关系
在 Spring 框架中, ApplicationEvent 是所有事件的基类,而 ApplicationListener 是监听这些事件的接口。它们之间的关系可以理解为“发布-订阅”模型:
- ApplicationEvent :代表事件本身,通常包含事件发生时的数据。
- ApplicationListener :定义了监听器如何响应特定类型的事件。
当某个组件发布一个 ApplicationEvent 时,Spring 会遍历所有注册的 ApplicationListener ,并调用匹配的监听器来处理事件。这种机制非常适合用于日志记录、异步任务处理、状态变更通知等场景。
3.1.2 Spring上下文中的事件发布流程
Spring 容器管理事件的整个生命周期,包括事件的创建、发布和监听。以下是 Spring 上下文中的事件发布流程:
- 事件创建 :开发者定义一个继承
ApplicationEvent的类,封装事件所需的数据。 - 监听器注册 :将实现
ApplicationListener接口的类注册为 Spring Bean。 - 事件发布 :通过
ApplicationEventPublisher或ApplicationContext发布事件。 - 事件处理 :Spring 会自动查找匹配的监听器并调用其
onApplicationEvent方法。
这一流程可以用以下 mermaid 流程图进行展示:
graph TD
A[事件创建] --> B[注册监听器]
B --> C[发布事件]
C --> D[Spring上下文查找监听器]
D --> E[调用onApplicationEvent方法]
E --> F[执行事件处理逻辑]通过上述流程,Spring 实现了事件驱动的异步通信机制,使得各模块之间可以解耦,提升了系统的可扩展性和可维护性。
3.2 实现基于ApplicationListener的观察者模式
接下来我们将通过一个具体的示例来演示如何在 Spring 中使用 ApplicationListener 实现观察者模式。
3.2.1 自定义事件类并继承ApplicationEvent
首先,我们需要定义一个自定义的事件类,并继承 ApplicationEvent 。这个类将用于封装事件发生时的相关数据。
import org.springframework.context.ApplicationEvent;
public class OrderCreatedEvent extends ApplicationEvent {
private final String orderId;
public OrderCreatedEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}代码解释:
OrderCreatedEvent继承了ApplicationEvent,表示这是一个自定义事件。- 构造函数接收两个参数:
source:事件的来源对象。orderId:订单 ID,表示当前创建的订单编号。- 提供
getOrderId()方法供监听器获取订单信息。
3.2.2 实现ApplicationListener接口处理事件
接下来,我们需要实现 ApplicationListener 接口,并指定监听的事件类型为 OrderCreatedEvent 。
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class OrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
String orderId = event.getOrderId();
System.out.println("订单创建事件已收到,订单编号:" + orderId);
// 这里可以添加订单创建后的业务逻辑,如发送邮件、记录日志等
}
}代码解释:
OrderCreatedEventListener类实现了ApplicationListener<OrderCreatedEvent>接口,表示它将监听OrderCreatedEvent类型的事件。@Component注解用于将该类注册为 Spring Bean。onApplicationEvent方法是监听器的回调方法,当有OrderCreatedEvent被发布时,该方法会被调用。- 在方法体内,获取事件中的订单 ID 并打印,同时可以添加额外的业务逻辑。
3.2.3 注册监听器并发布事件
最后,我们可以在某个业务类中发布事件。通常我们会通过 ApplicationEventPublisher 来发布事件。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createOrder(String orderId) {
System.out.println("正在创建订单,订单编号:" + orderId);
// 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(this, orderId));
}
}代码解释:
OrderService是一个业务类,用于创建订单。@Autowired注入了ApplicationEventPublisher,它是 Spring 提供的用于发布事件的接口。createOrder方法模拟创建订单的逻辑,并通过eventPublisher.publishEvent()发布OrderCreatedEvent。- 事件发布后,Spring 会自动调用所有监听该事件的监听器,比如前面定义的
OrderCreatedEventListener。
3.2.4 运行结果分析
当我们调用 OrderService.createOrder("1001") 时,控制台将输出:
正在创建订单,订单编号:1001 订单创建事件已收到,订单编号:1001
这说明事件已经成功发布,并被监听器捕获并处理。
3.3 ApplicationListener的高级用法
在实际开发中,我们可能需要处理多个事件、实现异步监听、或对事件进行过滤。Spring 提供了多种高级特性来支持这些需求。
3.3.1 多监听器与事件过滤机制
Spring 允许一个事件被多个监听器监听。我们可以通过为不同的监听器指定不同的事件类型,或者通过事件内容进行过滤。
例如,我们可以定义另一个监听器来监听订单取消事件:
public class OrderCanceledEvent extends ApplicationEvent {
private final String orderId;
public OrderCanceledEvent(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
}
@Component
public class OrderCanceledEventListener implements ApplicationListener<OrderCanceledEvent> {
@Override
public void onApplicationEvent(OrderCanceledEvent event) {
System.out.println("订单取消事件已收到,订单编号:" + event.getOrderId());
}
}事件过滤机制:
我们还可以通过事件的内容来决定是否处理该事件。例如,只处理特定订单状态的事件:
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
if (event.getOrderId().startsWith("VIP")) {
System.out.println("VIP订单创建事件:" + event.getOrderId());
}
}3.3.2 事件广播与异步监听支持
Spring 从 4.1 版本开始支持异步监听器。我们可以通过 @Async 注解实现事件的异步处理。
步骤:
- 启用异步支持:
@Configuration
@EnableAsync
public class AsyncConfig {
}
- 在监听器中添加
@Async注解:
@Component
public class AsyncOrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
@Async
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
System.out.println("异步监听器收到订单创建事件:" + event.getOrderId());
}
}优点:
- 提升系统响应速度,避免阻塞主线程。
- 支持并发处理多个事件。
3.3.3 多监听器执行顺序控制
如果多个监听器监听同一事件,可以通过 @Order 注解或实现 Ordered 接口来控制执行顺序:
@Component
@Order(1)
public class FirstOrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
...
}
@Component
@Order(2)
public class SecondOrderCreatedEventListener implements ApplicationListener<OrderCreatedEvent> {
...
}执行顺序表:
| 监听器类名 | 执行顺序 |
|---|---|
| FirstOrderCreatedEventListener | 第1位 |
| SecondOrderCreatedEventListener | 第2位 |
这种方式可以确保监听器按照我们期望的顺序执行。
3.3.4 事件广播机制
Spring 支持事件广播,即一个事件可以触发多个监听器,甚至触发其他事件。例如:
@Component
public class OrderCreatedEventBroadcaster implements ApplicationListener<OrderCreatedEvent> {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
System.out.println("广播订单创建事件");
eventPublisher.publishEvent(new CustomNotificationEvent(this, "订单已创建"));
}
}在这个例子中,当收到 OrderCreatedEvent 时,会广播一个新的 CustomNotificationEvent ,从而触发更多的监听器。
通过以上内容的详细讲解,我们系统性地掌握了 Spring 中 ApplicationListener 的基本使用和高级用法。从事件的定义、监听器的实现、事件发布机制,到多监听器管理、异步监听、执行顺序控制以及事件广播等高级功能,都展示了 Spring 框架在事件驱动开发中的强大能力。
4. 自定义观察者模式实现流程
在实际开发中,虽然Java提供了内置的 Observable 和 Observer 类,以及Spring框架中的 ApplicationEvent 和 ApplicationListener 机制,但在某些特定场景下,开发者仍需自定义观察者模式的实现,以满足更高的灵活性、可扩展性和性能要求。本章将深入讲解如何从零开始设计并实现一个完整的观察者模式系统,包括可观察对象(Subject)与观察者(Observer)的接口设计、核心实现逻辑、响应机制以及事件通知流程的优化策略。
4.1 自定义观察者模式的设计思路
自定义观察者模式的第一步是明确其核心设计思想。观察者模式本质上是“发布-订阅”机制的一种实现,其中“可观察对象”负责维护观察者列表,并在状态变化时通知所有注册的观察者。
4.1.1 可观察对象(Subject)的接口设计
可观察对象的核心职责包括:注册观察者、移除观察者、通知观察者。因此,其接口设计应包含以下方法:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
方法说明:
registerObserver(Observer observer):将观察者注册到可观察对象中,后续状态变化时将收到通知。removeObserver(Observer observer):移除某个观察者。notifyObservers():通知所有已注册的观察者,通常在Subject状态发生变更时调用。
4.1.2 观察者(Observer)接口的设计原则
观察者接口定义了观察者的响应方式。通常,每个观察者都需要实现一个统一的更新方法:
public interface Observer {
void update(String message);
}
设计原则:
- 单一职责原则 :每个观察者只处理与其相关的更新逻辑。
- 解耦性 :观察者不关心Subject的内部实现,仅通过
update方法接收通知。 - 扩展性 :支持多种观察者类型,便于后期扩展。
4.2 可观察对象(Subject)的实现细节
在接口设计完成后,下一步是实现具体的Subject类,包括管理观察者列表的数据结构,以及添加、移除和通知的方法。
4.2.1 管理观察者列表的数据结构
观察者列表建议使用线程安全的集合,如 CopyOnWriteArrayList ,以避免并发修改异常:
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcreteSubject implements Subject {
private List<Observer> observers = new CopyOnWriteArrayList<>();
@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("Subject状态已变更");
}
}
// 模拟Subject状态变化并触发通知
public void changeState() {
System.out.println("Subject状态发生变化");
notifyObservers();
}
}逻辑分析:
- 使用
CopyOnWriteArrayList确保线程安全,适用于读多写少的场景。 changeState()方法模拟Subject状态变化,触发通知。
4.2.2 添加、移除与通知观察者的具体实现
上述代码已实现观察者的注册、移除与通知。为了验证其实现,我们可以创建多个观察者并测试其行为。
4.3 观察者(Observer)接口设计与响应机制
观察者的响应机制决定了其如何处理通知信息。本节将介绍 update 方法的定义方式、参数传递策略以及多个观察者的响应顺序与处理策略。
4.3.1 update方法的定义与参数传递方式
update 方法可以传递多种类型的信息,如状态变化的具体内容、时间戳、上下文对象等:
public interface Observer {
void update(String message, LocalDateTime timestamp);
}
参数说明:
message:描述状态变化的具体内容。timestamp:记录事件发生的时间,便于后续日志或调试。
具体实现示例:
public class EmailObserver implements Observer {
@Override
public void update(String message, LocalDateTime timestamp) {
System.out.println("[" + timestamp + "] EmailObserver收到通知:" + message);
}
}
4.3.2 多个观察者的响应顺序与处理策略
当多个观察者同时注册时,其响应顺序默认是注册顺序。可以通过以下方式控制响应顺序:
| 观察者类型 | 响应顺序 | 说明 |
|---|---|---|
| EmailObserver | 先注册先执行 | 用于发送通知邮件 |
| SMSObserver | 中间注册 | 用于发送短信通知 |
| LogObserver | 最后注册 | 用于记录日志 |
响应顺序控制策略:
- 优先级机制 :为观察者设置优先级字段,按优先级排序后通知。
- 异步执行 :使用线程池并发执行各观察者的
update方法。
4.4 观察者注册与事件通知流程优化
在高并发或高性能场景下,需要对观察者注册与事件通知流程进行优化,以提升系统吞吐量和响应能力。
4.4.1 引入线程池实现异步通知
为了防止通知过程阻塞主线程,可以使用线程池实现异步通知:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncSubject implements Subject {
private List<Observer> observers = new CopyOnWriteArrayList<>();
private ExecutorService executor = Executors.newCachedThreadPool();
@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) {
executor.submit(() -> observer.update("异步通知:Subject状态已变更", LocalDateTime.now()));
}
}
public void changeState() {
System.out.println("Subject状态发生变化(异步)");
notifyObservers();
}
}逻辑分析:
- 使用
ExecutorService提交任务,实现异步通知。 - 避免阻塞主线程,提升响应速度。
4.4.2 事件过滤与观察者优先级设定
在某些场景下,并非所有观察者都需要响应每一个事件。可以通过事件类型或上下文信息进行过滤。
事件过滤机制示例:
public interface Observer {
boolean supports(EventType type);
void handle(Event event);
}
public enum EventType {
DATA_UPDATE, SYSTEM_ALERT, USER_ACTION
}
public class DataUpdateObserver implements Observer {
@Override
public boolean supports(EventType type) {
return type == EventType.DATA_UPDATE;
}
@Override
public void handle(Event event) {
System.out.println("处理数据更新事件:" + event.getContent());
}
}观察者优先级设定流程图:
graph TD
A[事件发生] --> B{观察者是否支持事件类型?}
B -->|是| C[调用handle方法处理事件]
B -->|否| D[跳过该观察者]
C --> E[根据优先级排序观察者]
D --> F[事件处理结束]
优势:
- 减少不必要的通知,提升性能。
- 支持更细粒度的事件处理逻辑。
本章总结
本章详细介绍了如何从零开始构建一个完整的自定义观察者模式系统。从接口设计到核心实现,再到响应机制与性能优化,逐步引导读者理解观察者模式的底层原理与实际应用。通过引入线程池实现异步通知、使用事件过滤机制与优先级设定,提升了系统的可扩展性与执行效率。下一章将结合实际案例,展示观察者模式在GUI开发、数据模型同步等场景中的具体应用。
5. 观察者模式的应用与测试
5.1 观察者模式在GUI开发中的应用
观察者模式在图形用户界面(GUI)开发中被广泛使用,特别是在事件驱动编程模型中。例如,在Java的Swing框架中,按钮点击事件就是通过观察者模式实现的。当用户点击按钮时,所有注册的监听器都会收到通知并执行相应的逻辑。
5.1.1 按钮点击与界面组件的事件监听
以下是一个使用Swing实现按钮点击监听的简单示例:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ButtonClickListenerExample {
public static void main(String[] args) {
JFrame frame = new JFrame("Observer in GUI");
JButton button = new JButton("Click Me");
// 注册观察者(监听器)
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
frame.add(button);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}- 代码说明 :
JFrame是GUI的主窗口。JButton是一个可点击的按钮控件。addActionListener()方法用于注册一个观察者(监听器)。- 每次点击按钮时,
actionPerformed()方法会被调用。 - 执行逻辑 :
- 1. 程序启动后创建窗口和按钮。
- 2. 用户点击按钮时,触发事件并通知所有监听器。
- 3. 控制台输出“Button clicked!”。
5.1.2 MVC架构中模型与视图的联动机制
在MVC(Model-View-Controller)架构中,模型(Model)和视图(View)之间通过观察者模式进行联动。当模型状态发生变化时,视图会自动更新。
以下是一个简化版的MVC示例:
import java.util.ArrayList;
import java.util.List;
// 模型
class DataModel {
private int value;
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void setValue(int value) {
this.value = value;
notifyObservers();
}
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(value);
}
}
}
// 观察者接口
interface Observer {
void update(int value);
}
// 视图
class DataView implements Observer {
private String name;
public DataView(String name) {
this.name = name;
}
@Override
public void update(int value) {
System.out.println(name + " updated to value: " + value);
}
}
public class MVCExample {
public static void main(String[] args) {
DataModel model = new DataModel();
DataView view1 = new DataView("View 1");
DataView view2 = new DataView("View 2");
model.addObserver(view1);
model.addObserver(view2);
model.setValue(10); // 触发更新
model.setValue(20);
}
}- 执行结果 :
View 1 updated to value: 10 View 2 updated to value: 10 View 1 updated to value: 20 View 2 updated to value: 20
- 逻辑说明 :
DataModel是数据模型,持有观察者列表。DataView是视图组件,实现Observer接口。- 当调用
setValue()方法时,所有注册的视图都会收到通知并更新。
5.2 观察者模式在数据模型更新中的应用
观察者模式非常适合用于数据模型的状态变更通知,尤其是在需要多个模块同步状态的场景中。
5.2.1 数据变更通知与缓存同步机制
在实际系统中,数据变更后可能需要同步多个缓存模块。观察者模式可以实现这种同步机制。
例如,当数据库中的订单状态变更后,可以通知所有缓存服务进行更新:
import java.util.ArrayList;
import java.util.List;
interface OrderObserver {
void onOrderUpdated(String orderId, String status);
}
class OrderCache implements OrderObserver {
@Override
public void onOrderUpdated(String orderId, String status) {
System.out.println("Cache: Order " + orderId + " status changed to " + status);
}
}
class InventoryCache implements OrderObserver {
@Override
public void onOrderUpdated(String orderId, String status) {
System.out.println("Inventory: Order " + orderId + " status changed to " + status);
}
}
class OrderService {
private List<OrderObserver> observers = new ArrayList<>();
public void addObserver(OrderObserver observer) {
observers.add(observer);
}
public void updateOrderStatus(String orderId, String status) {
System.out.println("OrderService: Updating order " + orderId + " to " + status);
notifyObservers(orderId, status);
}
private void notifyObservers(String orderId, String status) {
for (OrderObserver observer : observers) {
observer.onOrderUpdated(orderId, status);
}
}
}
public class CacheSyncExample {
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.addObserver(new OrderCache());
orderService.addObserver(new InventoryCache());
orderService.updateOrderStatus("1001", "Shipped");
}
}- 执行结果 :
OrderService: Updating order 1001 to Shipped Cache: Order 1001 status changed to Shipped Inventory: Order 1001 status changed to Shipped
- 逻辑分析 :
OrderService作为被观察对象,维护观察者列表。OrderCache和InventoryCache是两个观察者,监听订单状态变化。- 每次调用
updateOrderStatus()方法,都会通知所有观察者进行处理。
5.2.2 多模块间状态同步与解耦策略
通过观察者模式,可以实现模块间的松耦合。观察者无需知道被观察者的具体实现,只需注册监听接口即可。
| 模块 | 职责 | 耦合度 |
|---|---|---|
| OrderService | 发布订单状态变更事件 | 低 |
| OrderCache | 监听并更新缓存 | 低 |
| InventoryCache | 监听库存状态变化 | 低 |
这种设计方式可以有效降低模块之间的耦合度,提高系统的可维护性和可扩展性。
5.3 观察者模式的测试与完整代码示例
为了验证观察者模式的正确性,我们可以通过单元测试来模拟不同场景下的行为。
5.3.1 单元测试设计与Mock对象的使用
使用JUnit进行测试,配合Mockito框架模拟观察者对象:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.List;
import static org.mockito.Mockito.*;
class SubjectTest {
private Subject subject;
private Observer mockObserver1;
private Observer mockObserver2;
@BeforeEach
void setUp() {
subject = new Subject();
mockObserver1 = Mockito.mock(Observer.class);
mockObserver2 = Mockito.mock(Observer.class);
}
@Test
void testNotifyObserversWhenDataChanges() {
subject.addObserver(mockObserver1);
subject.addObserver(mockObserver2);
subject.setData("Hello");
verify(mockObserver1, times(1)).update("Hello");
verify(mockObserver2, times(1)).update("Hello");
}
@Test
void testRemoveObserver() {
subject.addObserver(mockObserver1);
subject.addObserver(mockObserver2);
subject.removeObserver(mockObserver1);
subject.setData("World");
verify(mockObserver1, never()).update("World");
verify(mockObserver2, times(1)).update("World");
}
}- 测试说明 :
testNotifyObserversWhenDataChanges()验证数据变化时所有观察者都能收到通知。testRemoveObserver()验证移除观察者后不再接收通知。
5.3.2 完整示例:模拟订单状态变更通知系统
下面是一个完整的订单状态变更通知系统示例,包含被观察者、观察者及注册机制:
import java.util.ArrayList;
import java.util.List;
interface OrderStatusListener {
void onStatusChange(String orderId, String newStatus);
}
class Order implements Subject {
private String orderId;
private String status;
private List<OrderStatusListener> listeners = new ArrayList<>();
public Order(String orderId) {
this.orderId = orderId;
}
public void setStatus(String status) {
this.status = status;
notifyListeners();
}
@Override
public void addObserver(OrderStatusListener listener) {
listeners.add(listener);
}
@Override
public void removeObserver(OrderStatusListener listener) {
listeners.remove(listener);
}
@Override
public void notifyListeners() {
for (OrderStatusListener listener : listeners) {
listener.onStatusChange(orderId, status);
}
}
}
class EmailService implements OrderStatusListener {
@Override
public void onStatusChange(String orderId, String newStatus) {
System.out.println("EmailService: Order " + orderId + " is now " + newStatus);
}
}
class SmsService implements OrderStatusListener {
@Override
public void onStatusChange(String orderId, String newStatus) {
System.out.println("SmsService: Order " + orderId + " is now " + newStatus);
}
}
public class OrderStatusExample {
public static void main(String[] args) {
Order order = new Order("1001");
EmailService emailService = new EmailService();
SmsService smsService = new SmsService();
order.addObserver(emailService);
order.addObserver(smsService);
order.setStatus("Processing");
order.setStatus("Shipped");
}
}5.3.3 测试结果分析与模式扩展建议
运行结果:
EmailService: Order 1001 is now Processing SmsService: Order 1001 is now Processing EmailService: Order 1001 is now Shipped SmsService: Order 1001 is now Shipped
- 测试分析 :
- 每次订单状态变更时,所有观察者都收到了通知。
可以轻松扩展更多观察者,如日志记录、库存更新等。
模式扩展建议 :
- 异步通知 :可引入线程池实现异步通知,提升系统响应速度。
- 事件优先级 :为不同观察者设置优先级,控制通知顺序。
- 过滤机制 :根据事件类型或订单ID进行监听器过滤,减少无效通知。
注:本章通过GUI开发、数据模型更新、单元测试与完整系统示例,深入展示了观察者模式在实际开发中的应用场景与实现方式,同时结合代码与流程图,帮助开发者理解其运行机制与扩展方向。
到此这篇关于观察者模式Java实现与设计模式实例代码详解的文章就介绍到这了,更多相关java观察者模式与设计模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
