一、前言
本文根据《Head first 设计模式》总结。
二、观察者模式引入
我们假设有这样的场景:
公司和某个气象台达成了合作关系,气象台为公司提供数据服务,并且他们打算为我们提供一个WeatherData对象来实现数据服务。他们主要提供的数据服务有实时天气、天气预测、气象分析等。现在我们打算根据他们提供的数据制作几个展板,这几个展板分别用来展示实时天气、天气预测、气象分析。
公司的程序猿小张得到需求后很快便给出了相应的代码实现:
public void onDataChange(){
humidity = getHumidity();
pressure = getPressure();
temperature = getTemperature();
currentInfo.update(temperature,humidity,pressure);
statisticsInfo.update(temperature,humidity,pressure);
forecastInfo.update(temperature,humidity,pressure);
}
需求确实很顺利的实现了,但是很快老板又要求小张添加新的展板用来展示其它的信息。于是小张又开始重复上面的代码。
很快,小张就厌倦了这种方式,并试图解决程序中的问题。那么,小张的程序中有哪些问题呢?
1、面向具体实现编程
所谓面向具体实现编程就是说代码只局限在具体场景中,没有通用性,如果添加新的需求很难去维护。
2、代码封装性不够
比如几个对象中都含有update方法,这时就可以考虑抽离出公共接口
三、认识观察者模式
在网络不太普及的年代,人们主要通过订阅报纸来获取外部消息。订阅报纸以后我们就无需关注报纸何时更新的相关问题。等到报纸更新以后报社会第一时间通知我们。观察者模式也是同样的道理。
当观察者注册到被观察者中以后,观察者就无需关注被观察者的最新状态,等都被观察者觉察到变化后就会通知观察者去更新最新的数据。
3.1 一对多依赖
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会接收到通知并自动更新。
3.2 松耦合
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。
对于观察者,主题只知道它实现了某一个接口,但是并不清楚观察者具体的实现细节。无论任何时候,我们都可以向主题中添加或移除一个观察者。因为主题只是依赖一个实现Observer接口的对象列表。无论我们添加或是删除某个观察者都不会影响到主题的运行。而且即使有新的观察者出现,主题也不需要修改任何代码,我们只需要向主题中添加观察者。主题不在乎别的,他只会发送通知给所有实现了观察者接口的对象。
如果我们在其它地方使用主题或者观察者,可以轻松的复用,因为二者不是紧耦合的。
设计原则:为了交互对象之间的松耦合设计而努力。
四、UML类图
五、实现观察者模式
5.1 接口设计
主题接口:
public interface Subject {
//注册观察者
void registerObserver(Observer observer);
//移除观察者
void removeObserver(Observer observer);
//通知观察者
void notifyObservers();
}
观察者:
public interface Observer {
//更新数据
void update();
}
公共显示接口:
public interface DisplayElement {
void display();
}
5.2 类设计
WeatherData.java
public class WeatherData implements Subject{
//观察者列表
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
private List<Float> forecastTemperatures;
public WeatherData(){
//初始化观察者列表
observers = new ArrayList<>();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
//当调用这个方法时,会通知所有观察者
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temperature, float humidity,
float pressure, List<Float> forecastTemperatures) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
this.forecastTemperatures = forecastTemperatures;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
public List<Float> getForecastTemperatures() {
return forecastTemperatures;
}
}
CurrentConditionsDisplay.java
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private WeatherData weatherData;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压
public CurrentConditionsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
//注册观察者
this.weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("当前温度为:" + this.temperature + "℃");
System.out.println("当前湿度为:" + this.humidity);
System.out.println("当前气压为:" + this.pressure);
}
@Override
public void update() {
this.temperature = this.weatherData.getTemperature();
this.humidity = this.weatherData.getHumidity();
this.pressure = this.weatherData.getPressure();
display();
}
}
5.3 测试
public class ObserverPatternTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(22f, 0.8f, 1.2f, null);
}
}