观察者模式
应该是比较好理解的一种设计模式。《Head Frist 设计模式》一书中对观察者模式
的解释就是:
出版者+订阅者=观察者模式
适用于需要监听对象变化的场景,拿《Head Frist 设计模式》书中的例子(天气更新)来熟悉一下该设计模式的应用。
天气更新需求:
有三块显示板,需要实时地显示天气数据的变化,三块显示板分别用于显示湿度、温度和气压。
需求分析:
三块显示板分别用于显示湿度、温度和气压,当前天气数据更新(暂时不需要关心天气数据如何更新)时,三块显示板的数据也需要更新。
那么根据需求分析,可以提取如下类关系:
其中class WeatherData
是客户提供的已有类,已知当天气数据变化时,measurementsChanged()会被调用
,那么如果需要在天气数据变化时,更新显示面板,那么最简单直接的做法,就是在measurementsChanged()
中添加代码去更新三块面板的显示:
/**
* 当温度、湿度、压力变化时,下面的方法会被调用
*/
void measurementsChaged() {
// to do
DisplayBoardInterface tempratureDispalyBoard = new TempratureDisplayBoard();
DisplayBoardInterface humidDispalyBoard = new HumidityDisplayBoard();
DisplayBoardInterface pressureDispalyBoard = new PressureDisplayBoard();
tempratureDispalyBoard.update(this.temperature);
humidDispalyBoard.update(this.humidity);
pressureDispalyBoard.update(this.pressure);
}
这样做可以满足当前的需求,但是有几个问题:
- 以后客户可能会变更需求,如需要添加、删除、修改显示板,那么还需要再次修改
measurementsChaged()
,会再次破坏已有类class WeatherData
的封装,这样不符合开闭原则
。
为了尽量避免上述问题,需要将变化部分和不变部分分离
,这种场景下,就可以使用观察者模式
来设计:
-
WeatherData
是出版者
-
TempratureDisplayBoard
、HumidityDisplayBoard
、PressureDisplayBoard
是订阅者
出版者
会通知所有 订阅者
自身的变化。
那么使用观察者模式
如下修改class WeatherData
代码:
package com.headfirst.strategy;
import java.util.ArrayList;
public class WeatherData {
/* 温度 */
float temperature;
/* 湿度*/
float humidity;
/* 压力 */
float pressure;
/* 保存所有的观察者 */
ArrayList<DisplayBoardInterface> observers = new ArrayList<DisplayBoardInterface>();
/* 注册观察者 */
public void registerObserver(DisplayBoardInterface observer) {
observers.add(observer);
}
/* 取消观察者 */
public void unregisterObserver(DisplayBoardInterface observer) {
observers.remove(observer);
}
/**
* 当温度、湿度、压力变化时,下面的方法会被调用
*/
void measurementsChaged() {
// to do
for (DisplayBoardInterface observer:observers) {
observer.update(this);
}
}
public float getTemprature() {
return this.temperature;
}
public float getHumidity() {
return this.humidity;
}
public float getPressure() {
return this.pressure;
}
public void setTemprature(float value) {
this.temperature = value;
measurementsChaged();
}
public void setHumidity(float value) {
this.humidity = value;
measurementsChaged();
}
public void setPressure(float value) {
this.pressure = value;
measurementsChaged();
}
}
通过注册接口添加观察者
(此处只是测试,未考虑取消注册,实际编程时需要注意取消注册,以防泄露):
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
//System.out.println("hello world.");
/**
* 更新天气信息
*/
WeatherData weatherData = new WeatherData();
/* 注册观察者 */
weatherData.registerObserver(new TempratureDisplayBoard());
weatherData.registerObserver(new HumidityDisplayBoard());
weatherData.registerObserver(new PressureDisplayBoard());
weatherData.setTemprature(37);
weatherData.setHumidity(20);
weatherData.setPressure(1000);
}
}
这样后续扩展新的观察者
,就不需要再次修改class WeatherData
的代码。