使用 PropertyChangeListener 和 PropertyChangeSupport
1. 创建 "被观察者" (Subject / Observable)
我们创建一个 NewsAgency 类。它不再继承 Observable,而是内部持有一个 PropertyChangeSupport 对象来管理所有的监听器。
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
// "观察者" - 新闻频道
public class NewsChannel implements PropertyChangeListener {
private String news;
private String channelName;
public NewsChannel(String channelName) {
this.channelName = channelName;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
// 当接收到通知时,更新自己的消息
this.news = (String) evt.getNewValue();
System.out.println(channelName + " 收到最新消息: " + this.news);
System.out.println(" (事件源: " + evt.getPropertyName() + ", 旧消息: " + evt.getOldValue() + ")");
}
}
代码解析:
NewsAgency类中包含一个PropertyChangeSupport实例。add/removePropertyChangeListener方法直接委托给support对象来处理监听器的注册和注销。- 当
setNews方法被调用时,它会使用support.firePropertyChange来通知所有已注册的监听器。这个方法可以传递属性名、旧值和新值,提供了比旧Observer模式更丰富的上下文信息。 [Observer is deprecated in Java 9. What should we use instead of it ...]
2. 创建 "观察者" (Observer)
我们创建一个 NewsChannel 类,它实现了 PropertyChangeListener 接口。
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
// "观察者" - 新闻频道
public class NewsChannel implements PropertyChangeListener {
private String news;
private String channelName;
public NewsChannel(String channelName) {
this.channelName = channelName;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
// 当接收到通知时,更新自己的消息
this.news = (String) evt.getNewValue();
System.out.println(channelName + " 收到最新消息: " + this.news);
System.out.println(" (事件源: " + evt.getPropertyName() + ", 旧消息: " + evt.getOldValue() + ")");
}
}
代码解析:
NewsChannel实现了PropertyChangeListener接口。- 核心逻辑在
propertyChange方法中。当NewsAgency触发事件时,这个方法会被自动调用。 PropertyChangeEvent对象evt包含了所有事件相关的信息,如事件源、属性名、旧值和新值,非常方便。
3. 运行示例
最后,我们编写一个 main 方法来将它们连接起来并测试。
public class Main {
public static void main(String[] args) {
// 创建被观察者(新闻机构)
NewsAgency agency = new NewsAgency();
// 创建观察者(新闻频道)
NewsChannel channel1 = new NewsChannel("第一频道");
NewsChannel channel2 = new NewsChannel("第二频道");
// 将观察者注册到被观察者
agency.addPropertyChangeListener(channel1);
agency.addPropertyChangeListener(channel2);
// 发布第一条新闻
System.out.println("--- 发布第一条新闻 ---");
agency.setNews("Java 25 正式发布!");
System.out.println("\n--- 第二频道取消订阅 ---");
agency.removePropertyChangeListener(channel2);
// 发布第二条新闻
System.out.println("\n--- 发布第二条新闻 ---");
agency.setNews("观察者模式有了新的实现方式。");
}
}
预期输出:
复制代码
--- 发布第一条新闻 --- 第一频道 收到最新消息: Java 25 正式发布! (事件源: news, 旧消息: null) 第二频道 收到最新消息: Java 25 正式发布! (事件源: news, 旧消息: null)
--- 第二频道取消订阅 ---
--- 发布第二条新闻 --- 第一频道 收到最新消息: 观察者模式有了新的实现方式。 (事件源: news, 旧消息: Java 25 正式发布!)
总结
与旧的 Observable/Observer 相比,使用 PropertyChangeListener 和 PropertyChangeSupport 的优势非常明显:
- 基于组合,更灵活:你的业务类无需为了实现观察者模式而继承一个特定的类,可以自由继承其他任何类。
- 更丰富的事件模型:
PropertyChangeEvent对象提供了更详细的上下文信息,包括属性名称、旧值和新值,而不仅仅是一个简单的“已更新”信号。 [Replacing Observer-Observable (inheritance vs. composition) · GitHub] - 遵循现代 Java 设计实践:这种方式更符合现代面向对象设计中“组合优于继承”的原则。
除了 java.beans 包,在更复杂的应用或响应式编程场景中,开发者也常常会使用功能更强大的第三方库,如 RxJava 或在 Kotlin 中的 Flow,它们提供了对观察者模式更高级、更强大的实现。