前言
大家好,我是练习两年半的Java练习生,最近阅读了《深入浅出设计模式(中文版)》,学习了各种设计模式,所以想出一个专栏和大家分享一下!
如果大家觉得文章还可以,欢迎关注点赞!后续还会陆续更新!!
一、定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
二、类图
实现观察者不止一种,但常见的是Subject 和 Observer
-
之间的依赖是什么?
主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新。
主题只知道 观察者 实现了某个接口(也就是Observer接口),主题不需要知道观察者的具体类是谁,做了什么
因为主题唯一依赖的东西是一个实现了Observer接口的对象列表,所以我们随时可以增加观察者,不会对主题产生影响。 -
使用到的原则:为了交互对象之间的松耦合设计而努力。
体现在改变主题或观察者其中的一方,并不会影响另一方,两者之间是松耦合的
三、应用
需求
此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。
分析
我们拿到这个weatherData ,可能会使用下面的方法进行
错误示范
public class WeatherData{
//实例变量声明
public void measurementsChanged(){
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionDisplay.update(temp,humidity,pressure);
statisticDisplay.update(temp,humidity,pressure);
forecastDisplay.update(temp,humidity,pressure);
}
}
如果这么做的话,我们每次增加一个观察者的时候,就需要修改这个方法,违背了对修改关闭,对扩展开放原则。
实现
首先,我们定义一个主题(Subject)接口,该接口定义了添加观察者、删除观察者、通知观察者等方法:
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
接下来,我们定义一个具体主题(具体气象站)类 WeatherData,该类实现了主题接口,并存储了温度、湿度和气压的值,并提供了相应的getter和setter方法:
public class WeatherData implements Subject {
private float temperature;
private float humidity;
private float pressure;
private List<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
int index = observers.indexOf(observer);
if (index >= 0) {
observers.remove(index);
}
}
public 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();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
接下来,我们定义一个观察者(Observer)接口,该接口定义了更新方法:
public interface Observer {
void update(float temperature, float humidity, float pressure);
}
最后,我们定义一个具体观察者(具体手机App、具体电视机)类 CurrentConditionsDisplay,该类实现了观察者接口,并实现了更新方法,当气象数据发生变化时,该类会更新温度、湿度和气压的值:
public class CurrentConditionsDisplay implements Observer {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity and " + pressure + " pressure");
}
}
在这个示例中,我们创建了一个WeatherData对象作为主题,并创建了一个CurrentConditionsDisplay对象作为观察者。当WeatherData的气象数据发生变化时,它会自动通知所有观察者对象进行更新,例如CurrentConditionsDisplay对象会更新自己的温度、湿度和气压的值并进行显示。
可以看到,观察者模式能够帮助我们实现对象之间的松耦合,让对象之间的交互更加灵活和可扩展。在实际开发中,我们可以将观察者模式应用于各种场景,例如GUI编程中的事件处理、消息通知系统等。
使用Java的观察者模式
其实,观察者模式在消息处理上有两种方式,一种是主题推送模式,像我们的微信公众号一下,推送消息给关注该公众号的人。而另外一种是拉取模式,就是有需要才调用拉取主题的信息
在java.util包内包含最基本的Observer和Observable类,这个和Subject和Observer相似,甚至更方便一些,因为许多功能都已经准备好了。
什么时候是拉消息?什么时候是推消息呢?
我们来看看java中Observable中的notifyObservers()方法
可知,我们是用过changed来设置是推模式还是拉模式。默认情况下,java是拉模式,如果调用了setChanged()方法,就更改为推模式
public class GeneralDisplay implements Observer {
private float temperature;
private float humidity;
private float pressure;
@Override
public void update(Observable o, Object arg) {
System.out.println("更新观察者数据");
WeatherData data = (WeatherData) arg;
temperature = data.getTemperature();
humidity = data.getHumidity();
pressure = data.getPressure();
display();
}
public void display(){
System.out.println("温度 : " + temperature + "湿度:" + humidity + "气压:" + pressure);
}
}
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public void measurementsChanged(float temperature,float humidity,float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
setChanged();
notifyObservers(this);
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
weatherData.setTemperature(12);
weatherData.setHumidity(22);
weatherData.setPressure(32);
GeneralDisplay generalDisplay = new GeneralDisplay();
weatherData.addObserver(generalDisplay);
weatherData.measurementsChanged(11,22,33);
weatherData.measurementsChanged(1,2,3);
}
}
四、问题
如何添加一个新的观察者?需要做哪些事情?
五、总结
以上就是今天要讲的内容,本文介绍了设计模式中的观察者模式,这个模式是实现对象之间的一对多依赖,订阅主题、主题推送是我们这节的核心内容。我们要记住一个原则,为了交互对象之间的松耦合设计而努力。
同时,在Java中提供的观察者也需要了解一下,明白什么是推模式、什么是拉模式。