观察者模式 (Observer Pattern)
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象(即观察者)都会得到通知并自动更新。观察者模式常用于事件处理系统、发布/订阅模式等场景中。
1. 观察者模式的组成
观察者模式主要由以下角色组成:
- Subject(主题或被观察者): 被观察的对象,它保持所有观察者的列表,并在自身状态变化时通知所有观察者。
- Observer(观察者): 接受通知并根据主题的变化更新自己状态的对象。每个观察者都需要实现一个更新方法,用来响应主题的变化。
- ConcreteSubject(具体主题): 具体的被观察者,它实现了主题接口并通知观察者更新。
- ConcreteObserver(具体观察者): 具体的观察者,它对主题的变化作出反应,通常是更新自身的状态。
2. 观察者模式的工作流程
- 观察者对象向主题(被观察者)注册自己。
- 主题对象维护一个观察者列表。
- 当主题状态发生变化时,它会通知所有注册的观察者。
- 观察者对象接收到通知后,更新自己的状态。
3. 观察者模式的实现
场景示例:天气预报系统
假设我们有一个天气预报系统,其中有多个用户(观察者)想要实时接收天气变化的通知。我们可以使用观察者模式来实现这个功能。
1) 定义观察者接口
观察者接口包含一个update
方法,用于接收主题的通知。
// 观察者接口
public interface Observer {
void update(String weather);
}
2) 定义主题接口
主题接口包含添加、移除观察者的方法和通知观察者的方法。
// 主题接口
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
3) 定义具体主题(WeatherStation)
ConcreteSubject
实现了 Subject
接口,并在状态发生变化时通知所有观察者。
import java.util.ArrayList;
import java.util.List;
// 具体主题
public class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private String weather;
// 添加观察者
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weather);
}
}
// 设置天气并通知观察者
public void setWeather(String weather) {
this.weather = weather;
notifyObservers();
}
}
4) 定义具体观察者(PhoneDisplay 和 DesktopDisplay)
每个观察者会在接收到通知时更新自己的状态。
// 具体观察者1:手机显示
public class PhoneDisplay implements Observer {
private String weather;
@Override
public void update(String weather) {
this.weather = weather;
display();
}
public void display() {
System.out.println("Phone Display: Current weather is " + weather);
}
}
// 具体观察者2:桌面显示
public class DesktopDisplay implements Observer {
private String weather;
@Override
public void update(String weather) {
this.weather = weather;
display();
}
public void display() {
System.out.println("Desktop Display: Current weather is " + weather);
}
}
5) 客户端代码
在客户端代码中,创建主题对象和观察者对象,并注册观察者。
public class Client {
public static void main(String[] args) {
// 创建天气站(主题)
WeatherStation weatherStation = new WeatherStation();
// 创建观察者
Observer phoneDisplay = new PhoneDisplay();
Observer desktopDisplay = new DesktopDisplay();
// 注册观察者
weatherStation.addObserver(phoneDisplay);
weatherStation.addObserver(desktopDisplay);
// 更新天气,通知所有观察者
weatherStation.setWeather("Sunny");
weatherStation.setWeather("Rainy");
}
}
运行结果:
Phone Display: Current weather is Sunny
Desktop Display: Current weather is Sunny
Phone Display: Current weather is Rainy
Desktop Display: Current weather is Rainy
4. 观察者模式的优点
- 解耦: 主题和观察者之间是松耦合的,主题不需要知道观察者的具体类,只知道它们实现了
Observer
接口。 - 动态更新: 当主题状态变化时,所有注册的观察者都会自动更新,无需手动调用每个观察者。
- 扩展性: 可以方便地增加新的观察者,且无需修改已有代码,符合开闭原则。
5. 观察者模式的缺点
- 多次通知: 当有很多观察者时,主题每次更新时都需要通知所有观察者,这可能导致性能问题。
- 依赖性: 虽然观察者和主题松耦合,但主题的改变仍然会影响到观察者的行为,这使得系统中仍然存在一定的依赖关系。
- 可能导致内存泄漏: 如果观察者未能正确地从主题中注销,可能会导致内存泄漏,因为主题仍然持有对观察者的引用。
6. 观察者模式的应用场景
- 事件监听器: GUI编程中的事件监听,用户点击按钮时,所有的监听器都会被触发。
- 发布/订阅系统: 例如,在消息队列、广播系统、RSS订阅等场景中,主题发布消息,所有订阅者(观察者)都接收该消息。
- 系统日志监控: 监控系统中的某个事件变化时,多个不同模块(观察者)需要做出响应。
7. 总结
观察者模式是一种非常常见且实用的设计模式,它通过一对多的方式,帮助实现对象之间的松耦合通信。主题和观察者的解耦使得它非常适用于动态变化和事件驱动的应用场景,特别是在GUI、消息通知、系统监控等领域具有广泛应用。