观察者模式
大约 11 分钟
观察者模式
什么是观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式也被称为发布-订阅模式(Publish-Subscribe Pattern)。
观察者模式的核心思想是:
- 定义对象间的一对多依赖关系
- 当一个对象状态改变时,所有依赖对象都会得到通知
- 依赖对象能够自动更新自己的状态
为什么需要观察者模式
在实际开发中,我们经常需要实现当一个对象的状态发生变化时,其他相关对象能够自动得到通知并进行相应的处理。如果直接在被观察对象中维护对所有观察者的引用并直接调用它们的方法,会导致系统耦合度高,难以扩展和维护。观察者模式通过定义观察者接口和被观察者接口,实现了两者之间的解耦,使得系统更加灵活和可扩展。
观察者模式的结构
观察者模式包含以下几个角色:
- 主题(Subject):也称为被观察者,它知道它的观察者,可以添加或删除观察者
- 具体主题(ConcreteSubject):实现主题接口,当内部状态改变时,向所有观察者发出通知
- 观察者(Observer):定义一个更新接口,用于接收主题的通知
- 具体观察者(ConcreteObserver):实现观察者接口,保存一个指向具体主题的引用,实现更新接口
观察者模式的实现
基本实现
// 观察者接口
interface Observer {
void update(String message);
}
// 主题接口
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
// 改变状态并通知观察者
public void setState(String state) {
this.state = state;
System.out.println("主题状态改变为: " + state);
notifyObservers("状态更新: " + state);
}
public String getState() {
return state;
}
}
// 具体观察者A
class ConcreteObserverA implements Observer {
private String name;
public ConcreteObserverA(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println("观察者A (" + name + ") 收到通知: " + message);
}
}
// 具体观察者B
class ConcreteObserverB implements Observer {
private String name;
public ConcreteObserverB(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println("观察者B (" + name + ") 收到通知: " + message);
}
}
// 使用示例
public class ObserverDemo {
public static void main(String[] args) {
// 创建主题
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者
Observer observerA = new ConcreteObserverA("观察者A");
Observer observerB = new ConcreteObserverB("观察者B");
// 注册观察者
subject.addObserver(observerA);
subject.addObserver(observerB);
// 改变主题状态
System.out.println("=== 观察者模式演示 ===");
subject.setState("新状态1");
// 移除一个观察者
subject.removeObserver(observerA);
// 再次改变状态
subject.setState("新状态2");
}
}
改进的实现(带状态信息)
// 改进的观察者接口
interface ImprovedObserver {
void update(ImprovedSubject subject);
}
// 改进的主题接口
interface ImprovedSubject {
void addObserver(ImprovedObserver observer);
void removeObserver(ImprovedObserver observer);
void notifyObservers();
}
// 改进的具体主题
class ImprovedConcreteSubject implements ImprovedSubject {
private List<ImprovedObserver> observers = new ArrayList<>();
private String state;
@Override
public void addObserver(ImprovedObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(ImprovedObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (ImprovedObserver observer : observers) {
observer.update(this);
}
}
// 改变状态并通知观察者
public void setState(String state) {
this.state = state;
System.out.println("主题状态改变为: " + state);
notifyObservers();
}
public String getState() {
return state;
}
}
// 改进的具体观察者A
class ImprovedConcreteObserverA implements ImprovedObserver {
private String name;
public ImprovedConcreteObserverA(String name) {
this.name = name;
}
@Override
public void update(ImprovedSubject subject) {
if (subject instanceof ImprovedConcreteSubject) {
ImprovedConcreteSubject concreteSubject = (ImprovedConcreteSubject) subject;
System.out.println("观察者A (" + name + ") 收到通知,当前状态: " + concreteSubject.getState());
}
}
}
// 改进的具体观察者B
class ImprovedConcreteObserverB implements ImprovedObserver {
private String name;
public ImprovedConcreteObserverB(String name) {
this.name = name;
}
@Override
public void update(ImprovedSubject subject) {
if (subject instanceof ImprovedConcreteSubject) {
ImprovedConcreteSubject concreteSubject = (ImprovedConcreteSubject) subject;
System.out.println("观察者B (" + name + ") 收到通知,当前状态: " + concreteSubject.getState());
}
}
}
// 使用示例
public class ImprovedObserverDemo {
public static void main(String[] args) {
// 创建主题
ImprovedConcreteSubject subject = new ImprovedConcreteSubject();
// 创建观察者
ImprovedObserver observerA = new ImprovedConcreteObserverA("观察者A");
ImprovedObserver observerB = new ImprovedConcreteObserverB("观察者B");
// 注册观察者
subject.addObserver(observerA);
subject.addObserver(observerB);
// 改变主题状态
System.out.println("=== 改进的观察者模式演示 ===");
subject.setState("改进状态1");
// 移除一个观察者
subject.removeObserver(observerA);
// 再次改变状态
subject.setState("改进状态2");
}
}
观察者模式的应用场景
- GUI事件处理:按钮点击、鼠标移动等事件的处理
- 消息订阅系统:新闻发布、邮件订阅等
- MVC架构:Model和View之间的通信
- 游戏开发:游戏事件的处理和响应
- 股票价格监控:股价变化通知投资者
- 社交网络:用户动态更新通知关注者
观察者模式的优缺点
优点
- 降低耦合度:观察者和被观察者之间是抽象耦合关系
- 支持广播通信:被观察者会向所有注册的观察者发送通知
- 符合开闭原则:增加新的观察者无须修改原有代码
- 动态关联:可以在运行时建立和删除观察者与被观察者的关系
缺点
- 可能导致性能问题:如果观察者很多,通知所有观察者可能耗时较长
- 可能导致循环依赖:观察者和被观察者之间可能存在循环引用
- 可能导致内存泄漏:如果忘记移除观察者,可能导致内存泄漏
- 缺乏相应的机制让观察者知道所观察的目标对象是怎么发生变化的
观察者模式与其他模式的比较
与中介者模式的区别
- 观察者模式:定义对象间的一对多依赖关系
- 中介者模式:用中介对象来封装对象间的交互
与责任链模式的区别
- 观察者模式:所有符合条件的观察者都会收到通知
- 责任链模式:请求沿着链传递,直到被处理为止
与发布-订阅模式的区别
- 观察者模式:观察者直接订阅主题
- 发布-订阅模式:通过事件通道间接订阅,发布者和订阅者解耦更彻底
实际项目中的应用
// 天气预报系统示例
// 天气数据类
class WeatherData {
private float temperature;
private float humidity;
private float pressure;
// Getter方法
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
// Setter方法
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
}
}
// 天气观察者接口
interface WeatherObserver {
void update(WeatherData weatherData);
}
// 天气主题接口
interface WeatherSubject {
void addObserver(WeatherObserver observer);
void removeObserver(WeatherObserver observer);
void notifyObservers();
}
// 天气数据主题
class WeatherDataSubject implements WeatherSubject {
private List<WeatherObserver> observers;
private WeatherData weatherData;
public WeatherDataSubject() {
observers = new ArrayList<>();
weatherData = new WeatherData();
}
@Override
public void addObserver(WeatherObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(WeatherObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (WeatherObserver observer : observers) {
observer.update(weatherData);
}
}
// 设置天气测量数据
public void setMeasurements(float temperature, float humidity, float pressure) {
weatherData.setMeasurements(temperature, humidity, pressure);
measurementsChanged();
}
// 当测量数据改变时通知观察者
public void measurementsChanged() {
notifyObservers();
}
// 获取天气数据
public WeatherData getWeatherData() {
return weatherData;
}
}
// 当前天气显示
class CurrentConditionsDisplay implements WeatherObserver {
private float temperature;
private float humidity;
private WeatherDataSubject weatherData;
public CurrentConditionsDisplay(WeatherDataSubject weatherData) {
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void update(WeatherData weatherData) {
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
public void display() {
System.out.println("当前天气状况:");
System.out.println("温度: " + temperature + "°C");
System.out.println("湿度: " + humidity + "%");
System.out.println("--------------------");
}
}
// 天气统计显示
class StatisticsDisplay implements WeatherObserver {
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum = 0.0f;
private int numReadings;
private WeatherDataSubject weatherData;
public StatisticsDisplay(WeatherDataSubject weatherData) {
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void update(WeatherData weatherData) {
float temp = weatherData.getTemperature();
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
public void display() {
System.out.println("天气统计:");
System.out.println("平均温度: " + (tempSum / numReadings));
System.out.println("最高温度: " + maxTemp);
System.out.println("最低温度: " + minTemp);
System.out.println("--------------------");
}
}
// 天气预报显示
class ForecastDisplay implements WeatherObserver {
private float currentPressure = 29.92f;
private float lastPressure;
private WeatherDataSubject weatherData;
public ForecastDisplay(WeatherDataSubject weatherData) {
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void update(WeatherData weatherData) {
lastPressure = currentPressure;
currentPressure = weatherData.getPressure();
display();
}
public void display() {
System.out.println("天气预报:");
if (currentPressure > lastPressure) {
System.out.println("天气改善!");
} else if (currentPressure == lastPressure) {
System.out.println("天气不变");
} else if (currentPressure < lastPressure) {
System.out.println("天气变坏,可能有雨!");
}
System.out.println("--------------------");
}
}
// 使用示例
public class WeatherStationDemo {
public static void main(String[] args) {
// 创建天气数据主题
WeatherDataSubject weatherData = new WeatherDataSubject();
// 创建显示元素
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
// 模拟天气数据更新
System.out.println("=== 天气预报系统演示 ===");
System.out.println("1. 第一次天气更新:");
weatherData.setMeasurements(80, 65, 30.4f);
System.out.println("\n2. 第二次天气更新:");
weatherData.setMeasurements(82, 70, 29.2f);
System.out.println("\n3. 第三次天气更新:");
weatherData.setMeasurements(78, 90, 29.2f);
}
}
// 股票价格监控示例
// 股票数据类
class Stock {
private String symbol;
private double price;
public Stock(String symbol, double price) {
this.symbol = symbol;
this.price = price;
}
public String getSymbol() {
return symbol;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return symbol + ": $" + price;
}
}
// 股票观察者接口
interface StockObserver {
void update(Stock stock);
}
// 股票主题接口
interface StockSubject {
void addObserver(StockObserver observer);
void removeObserver(StockObserver observer);
void notifyObservers(Stock stock);
}
// 股票监控系统
class StockMonitor implements StockSubject {
private List<StockObserver> observers;
private Map<String, Stock> stocks;
public StockMonitor() {
observers = new ArrayList<>();
stocks = new HashMap<>();
}
@Override
public void addObserver(StockObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(StockObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(Stock stock) {
for (StockObserver observer : observers) {
observer.update(stock);
}
}
// 添加股票
public void addStock(Stock stock) {
stocks.put(stock.getSymbol(), stock);
}
// 更新股票价格
public void updateStockPrice(String symbol, double newPrice) {
Stock stock = stocks.get(symbol);
if (stock != null) {
stock.setPrice(newPrice);
System.out.println("股票价格更新: " + stock);
notifyObservers(stock);
}
}
// 获取股票
public Stock getStock(String symbol) {
return stocks.get(symbol);
}
}
// 投资者类
class Investor implements StockObserver {
private String name;
private Map<String, Double> portfolio;
public Investor(String name) {
this.name = name;
this.portfolio = new HashMap<>();
}
public void buyStock(String symbol, int quantity) {
portfolio.put(symbol, (double) quantity);
System.out.println(name + " 购买 " + quantity + " 股 " + symbol);
}
@Override
public void update(Stock stock) {
String symbol = stock.getSymbol();
if (portfolio.containsKey(symbol)) {
Double quantity = portfolio.get(symbol);
double value = stock.getPrice() * quantity;
System.out.println("投资者 " + name + " 持有的 " + symbol + " 当前价值: $" + String.format("%.2f", value));
}
}
}
// 股价警报系统
class PriceAlertSystem implements StockObserver {
private double alertThreshold;
public PriceAlertSystem(double alertThreshold) {
this.alertThreshold = alertThreshold;
}
@Override
public void update(Stock stock) {
if (stock.getPrice() >= alertThreshold) {
System.out.println("【警报】" + stock.getSymbol() + " 股价达到 $" + alertThreshold + ",当前价格: $" + stock.getPrice());
}
}
}
// 股票分析师
class StockAnalyst implements StockObserver {
private String name;
public StockAnalyst(String name) {
this.name = name;
}
@Override
public void update(Stock stock) {
System.out.println("分析师 " + name + " 分析: " + stock.getSymbol() + " 当前价格为 $" + stock.getPrice());
}
}
// 使用示例
public class StockMarketDemo {
public static void main(String[] args) {
// 创建股票监控系统
StockMonitor monitor = new StockMonitor();
// 添加股票
Stock appleStock = new Stock("AAPL", 150.0);
Stock googleStock = new Stock("GOOGL", 2800.0);
monitor.addStock(appleStock);
monitor.addStock(googleStock);
// 创建观察者
Investor investor1 = new Investor("张三");
investor1.buyStock("AAPL", 100);
investor1.buyStock("GOOGL", 10);
Investor investor2 = new Investor("李四");
investor2.buyStock("AAPL", 50);
PriceAlertSystem alertSystem = new PriceAlertSystem(160.0);
StockAnalyst analyst = new StockAnalyst("王分析师");
// 注册观察者
monitor.addObserver(investor1);
monitor.addObserver(investor2);
monitor.addObserver(alertSystem);
monitor.addObserver(analyst);
// 模拟股价变化
System.out.println("=== 股票市场监控演示 ===");
System.out.println("\n1. 苹果股价上涨:");
monitor.updateStockPrice("AAPL", 155.0);
System.out.println("\n2. 苹果股价继续上涨:");
monitor.updateStockPrice("AAPL", 165.0);
System.out.println("\n3. 谷歌股价下跌:");
monitor.updateStockPrice("GOOGL", 2750.0);
}
}
Java内置观察者模式
// Java内置观察者模式示例
import java.util.Observable;
import java.util.Observer;
// 使用Java内置Observable类
class NewsAgency extends Observable {
private String news;
public void setNews(String news) {
this.news = news;
setChanged(); // 标记状态已改变
notifyObservers(news); // 通知观察者
}
public String getNews() {
return news;
}
}
// 使用Java内置Observer接口
class NewsChannel implements Observer {
private String name;
public NewsChannel(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof NewsAgency) {
NewsAgency agency = (NewsAgency) o;
System.out.println(name + " 收到新闻: " + arg);
}
}
}
// 使用示例
public class JavaObserverDemo {
public static void main(String[] args) {
// 创建新闻机构
NewsAgency agency = new NewsAgency();
// 创建新闻频道
NewsChannel channel1 = new NewsChannel("CCTV新闻");
NewsChannel channel2 = new NewsChannel("新华网");
NewsChannel channel3 = new NewsChannel("人民网");
// 注册观察者
agency.addObserver(channel1);
agency.addObserver(channel2);
agency.addObserver(channel3);
// 发布新闻
System.out.println("=== Java内置观察者模式演示 ===");
agency.setNews("国家发布新的经济发展政策");
System.out.println();
agency.setNews("科技创新大会即将召开");
}
}
总结
观察者模式是一种非常实用的行为型设计模式,它定义了对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在实际开发中,当我们需要实现事件处理、消息订阅、状态监控等功能时,观察者模式是一个很好的选择。
使用观察者模式的关键点:
- 识别需要一对多依赖关系的对象
- 定义观察者接口和主题接口
- 实现具体观察者和具体主题类
- 在主题中维护观察者列表并实现通知机制
- 在观察者中实现更新逻辑
观察者模式的优点是能够降低对象间的耦合度,支持广播通信,符合开闭原则。但需要注意的是,如果观察者很多,可能会影响性能,还需要注意避免内存泄漏问题。在现代Java开发中,观察者模式广泛应用于GUI事件处理、消息订阅系统、MVC架构等场景。Java也提供了内置的观察者模式实现,但在新版本中已被标记为过时,推荐使用Java 9引入的Flow API来实现响应式编程。