文章目录
- 引言
- 观察者模式的定义
- 观察者模式的应用场景
- 观察者模式的基本概念
- 主题(Subject)和观察者(Observer)的关系
- 观察者模式的优缺点
- 优点
- 缺点
- Java中的观察者模式实现
- Java内置的观察者模式
- `java.util.Observer` 接口
- `java.util.Observable` 类
- 示例代码
- 自定义实现观察者模式
- 自定义接口和类
- 具体代码示例
- 使用 `java.util.Observer` 和 `java.util.Observable` 的示例
- 主题类(Observable)
- 观察者类(Observer)
- 测试类
- 自定义实现观察者模式的示例
- 主题接口
- 观察者接口
- 具体的主题实现类
- 具体的观察者实现类
- 测试类
- 观察者模式在实际项目中的应用
- 典型应用场景
- 观察者模式在 MVC 架构中的应用
- MVC 架构概述
- 示例代码
- Model 类
- View 类
- Controller 类
- 测试代码
- 小结
- 观察者模式的优缺点总结
- 优点
- 缺点
- 使用观察者模式的最佳实践
引言
观察者模式的定义
观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式通常用于实现事件处理系统。
通过观察者模式,主题(Subject)和观察者(Observer)之间的耦合度降低了。主题只需维护一个观察者列表,并负责通知观察者,而不需要关心观察者的具体实现。
观察者模式的应用场景
观察者模式广泛应用于需要事件通知的场景,以下是一些典型的应用场景:
-
GUI框架中的事件处理:
- 在图形用户界面(GUI)中,按钮、文本框等控件的事件处理通常使用观察者模式。例如,按钮的点击事件可以通知多个监听器执行相应的操作。
-
数据模型与视图的同步:
- 在MVC(Model-View-Controller)架构中,模型(Model)是主题,视图(View)是观察者。当模型的数据发生变化时,视图需要自动更新以反映最新的数据。
-
发布-订阅系统:
- 在消息系统中,发布者(Publisher)和订阅者(Subscriber)之间通常使用观察者模式。发布者发布消息,所有订阅了该消息的订阅者都会收到通知并处理消息。
-
日志系统:
- 在日志系统中,可以将不同的日志记录器(如文件记录器、控制台记录器)作为观察者,日志事件发生时,所有的日志记录器都会收到通知并记录日志。
观察者模式的基本概念
主题(Subject)和观察者(Observer)的关系
在观察者模式中,主要涉及两个角色:主题(Subject)和观察者(Observer)。
-
主题(Subject):
- 主题是被观察的对象。它维护了一组观察者,并在自身状态发生变化时通知这些观察者。
- 主题通常提供以下方法:
registerObserver(Observer o)
:注册一个观察者。removeObserver(Observer o)
:移除一个观察者。notifyObservers()
:通知所有注册的观察者。
-
观察者(Observer):
- 观察者是观察主题的对象。当主题的状态发生变化时,观察者会收到通知并进行相应的更新。
- 观察者通常实现一个
update
方法,用于接收主题的通知并更新自身状态。
观察者模式的优缺点
优点
-
解耦:
- 观察者模式将观察者与主题解耦,使得它们可以独立变化。主题不需要知道观察者的具体实现,只需通过接口进行通知。
-
灵活性:
- 可以在运行时动态添加或移除观察者,增强了系统的灵活性和可扩展性。
-
符合开闭原则:
- 观察者模式使得系统易于扩展。当需要增加新的观察者时,不需要修改现有的主题代码,只需实现新的观察者并注册到主题中即可。
缺点
-
可能导致性能问题:
- 当观察者数量较多时,通知所有观察者可能会导致性能问题。尤其是在观察者需要处理复杂逻辑时,整个通知过程可能会变得很慢。
-
可能导致内存泄漏:
- 如果主题维护的观察者列表没有及时清理(例如,观察者已经不再需要但没有被移除),可能会导致内存泄漏。
-
不确定的通知顺序:
- 通常情况下,观察者的通知顺序是不确定的。如果不同的观察者对通知的顺序有依赖性,可能会导致问题。
Java中的观察者模式实现
Java内置的观察者模式
Java 提供了一套内置的观察者模式实现,通过 java.util.Observer
接口和 java.util.Observable
类来实现。
java.util.Observer
接口
Observer
接口只有一个方法,需要观察者实现:
public interface Observer {void update(Observable o, Object arg);
}
java.util.Observable
类
Observable
类提供了一些方法来管理和通知观察者:
public class Observable {// 添加观察者public synchronized void addObserver(Observer o) { ... }// 删除观察者public synchronized void deleteObserver(Observer o) { ... }// 通知所有观察者public void notifyObservers() { ... }// 通知所有观察者,并传递参数public void notifyObservers(Object arg) { ... }// 标记状态已改变protected synchronized void setChanged() { ... }// 清除已改变标记protected synchronized void clearChanged() { ... }// 检查状态是否已改变public synchronized boolean hasChanged() { ... }// 获取观察者数量public synchronized int countObservers() { ... }
}
示例代码
import java.util.Observable;
import java.util.Observer;// 具体的主题类
class WeatherData extends Observable {private float temperature;private float humidity;private float pressure;public void measurementsChanged() {setChanged();notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}// 具体的观察者类
class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;public CurrentConditionsDisplay(Observable observable) {observable.addObserver(this);}@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherData) {WeatherData weatherData = (WeatherData) o;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}// 测试代码
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);}
}
自定义实现观察者模式
虽然 Java 内置的观察者模式实现已经很方便,但有时我们需要自定义实现以满足特定需求。以下是一个自定义实现的示例:
自定义接口和类
import java.util.ArrayList;
import java.util.List;// 观察者接口
interface Observer {void update(float temperature, float humidity, float pressure);
}// 主题接口
interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}// 具体的主题类
class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {observers.remove(o);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void measurementsChanged() {notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}// 具体的观察者类
class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;public CurrentConditionsDisplay(Subject weatherData) {weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}// 测试代码
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);}
}
具体代码示例
使用 java.util.Observer
和 java.util.Observable
的示例
首先,我们来看一个使用 Java 内置的 Observer
和 Observable
来实现观察者模式的示例。
主题类(Observable)
import java.util.Observable;public class WeatherData extends Observable {private float temperature;private float humidity;private float pressure;public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public void measurementsChanged() {setChanged();notifyObservers();}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}
观察者类(Observer)
import java.util.Observable;
import java.util.Observer;public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherData) {WeatherData weatherData = (WeatherData) o;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
测试类
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();weatherData.addObserver(currentDisplay);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
自定义实现观察者模式的示例
接下来,我们来看一个自定义实现观察者模式的示例。
主题接口
import java.util.ArrayList;
import java.util.List;public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}
观察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}
具体的主题实现类
public class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {observers.remove(o);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public void measurementsChanged() {notifyObservers();}
}
具体的观察者实现类
public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}
测试类
public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();weatherData.registerObserver(currentDisplay);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}
通过这两个示例,我们可以看到如何使用 Java 内置的 Observer
和 Observable
实现观察者模式,以及如何自定义实现观察者模式。
观察者模式在实际项目中的应用
典型应用场景
观察者模式在实际项目中有许多典型的应用场景,包括但不限于:
- 事件处理系统:在 GUI 工具包(如 Java Swing)中,观察者模式用于监听用户事件(如按钮点击、文本输入等)。
- 日志系统:在应用程序中,日志记录器可以作为观察者,监听应用程序的状态变化并记录相关信息。
- 通知系统:在社交网络、邮件系统或消息推送系统中,观察者模式用于通知用户关于某些事件的发生。
- 数据同步:在分布式系统中,观察者模式用于保证数据的一致性,当数据发生变化时,所有的副本都会同步更新。
观察者模式在 MVC 架构中的应用
MVC(Model-View-Controller)架构是一个非常经典的设计模式,广泛应用于各种软件开发中。观察者模式在 MVC 架构中扮演了重要角色,特别是在 Model 和 View 之间的交互中。
MVC 架构概述
- Model:负责数据和业务逻辑的处理。
- View:负责数据显示和用户界面的呈现。
- Controller:负责接收用户输入并调用 Model 和 View 来完成用户请求。
在 MVC 中,Model 作为主题(Subject),View 作为观察者(Observer)。当 Model 中的数据发生变化时,View 会自动更新,以反映新的数据状态。
示例代码
我们以一个简单的股票价格更新系统为例,展示观察者模式在 MVC 架构中的应用。
Model 类
import java.util.Observable;public class StockData extends Observable {private String stockSymbol;private float stockPrice;public StockData(String stockSymbol) {this.stockSymbol = stockSymbol;}public void setStockPrice(float stockPrice) {this.stockPrice = stockPrice;stockPriceChanged();}public void stockPriceChanged() {setChanged();notifyObservers();}public String getStockSymbol() {return stockSymbol;}public float getStockPrice() {return stockPrice;}
}
View 类
import java.util.Observable;
import java.util.Observer;public class StockDisplay implements Observer {private Observable stockData;private String stockSymbol;private float stockPrice;public StockDisplay(Observable stockData) {this.stockData = stockData;stockData.addObserver(this);}@Overridepublic void update(Observable o, Object arg) {if (o instanceof StockData) {StockData stockData = (StockData) o;this.stockSymbol = stockData.getStockSymbol();this.stockPrice = stockData.getStockPrice();display();}}public void display() {System.out.println("Stock: " + stockSymbol + " Price: " + stockPrice);}
}
Controller 类
public class StockController {private StockData stockData;public StockController(StockData stockData) {this.stockData = stockData;}public void updateStockPrice(float newPrice) {stockData.setStockPrice(newPrice);}
}
测试代码
public class StockMarket {public static void main(String[] args) {StockData stockData = new StockData("AAPL");StockDisplay stockDisplay = new StockDisplay(stockData);StockController stockController = new StockController(stockData);stockController.updateStockPrice(150.0f);stockController.updateStockPrice(155.0f);}
}
小结
观察者模式的优缺点总结
优点
- 松耦合:观察者模式使得观察者和主题之间的耦合度降低。主题不需要知道观察者的具体实现,只需要知道它们实现了观察者接口。
- 灵活性:可以在运行时动态添加和移除观察者,提供了很大的灵活性。
- 扩展性:可以很容易地添加新的观察者而不影响现有的系统。
- 一致性:当主题的状态发生变化时,所有的观察者都会自动接收到通知,保持数据的一致性。
缺点
- 性能开销:如果有大量的观察者,通知所有观察者可能会影响性能。
- 复杂性增加:管理观察者列表和通知机制会增加系统的复杂性,特别是在处理并发问题时。
- 潜在的内存泄漏:如果观察者没有正确地解除注册,可能会导致内存泄漏。
使用观察者模式的最佳实践
- 明确职责:确保主题和观察者各自的职责明确,主题只负责状态的管理和通知,观察者负责响应和处理通知。
- 避免过度使用:不要滥用观察者模式,尤其是在简单的场景中。过度使用会增加系统的复杂性。
- 处理并发问题:在多线程环境中,确保对观察者列表的操作是线程安全的,以避免并发问题。
- 解除注册:在适当的时候(如观察者不再需要接收通知时)确保观察者正确地解除注册,以避免内存泄漏。
- 通知优化:在大量观察者的情况下,可以考虑批量通知或延迟通知,以优化性能。