最近看了<Head First设计模式>里的观察者模式,代码逻辑并没有什么太花哨的东西,但是对于代码结构设计可扩展性有一定的启发.下面是书中内容的简单整理:
业务需求:
设计一个气象监测应用,在温度,气压,湿度变化时在公告板显示数据;
需求分析:
目的用于数据显示,所以可以将代码分为三块:监控设备,气象站数据,公告板
业务流程:监控设备获取原始信息->气象站获取原始信息->通知公告板信息变更显示
核心业务逻辑:
气象站代码实现:
public class WeatherData {
// 更新公告板数据
public void measurementsChanged() {
// 获取最新温度,气压,湿度数据
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
// 更新公告板信息
currentConditionsDisplay.update(temp,humidity,pressure);
}
}
其实实现功能就是当监控设备的数据变化时,调取公告板更新显示数据;
但是这个代码有一些问题:
针对具体实现编程,未针对接口编程,代码耦合性高,可扩展性较差;
所以这里可以用观察者模式进行规范,降低耦合性,增加扩展性;
观察者模式:定义对象间一对多依赖,当一个对象状态变化时,它的所有依赖着都会收到通知并自动更新;
类似于消息订阅,观察者统一在主题处注册,当有新消息时主题通知观察者,完成数据触发操作;
消息获取存在推,拉两种消息获取方式:
推:消息获取直接,但是信息量大,扩展时就要修改接口;
拉:消息获取较复杂,但是扩展方便;
观察者模式设计:
定义三个接口:Subject(主题),Observer(观察者),DisplayElement(显示元素);
定义具体实现类:WeatherData(气象数据)实现Subject接口;公告板实现Observer,DisplayElement接口;
代码实现:
主题接口:
// 管理接收信息的观察者,进行消息通知
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
// 激活观察者状态更新
void notifyObservers();
}
观察者接口:
// 观察者,进行消息更新
public interface Observer {
/**
* @Description: 更新观察者数据
* @param subject:主题
* @param obj:推送消息
*/
void update(Subject subject,Object obj);
}
数据显示接口:
public interface DisplayElement {
void display();
}
主题具体实现类:
public class WeatherData implements Subject {
private List<Observer> observers;
private float temp;
private float humidity;
private float pressure;
private boolean changed;// 更新标识
public WeatherData() {
// 实例初始化再创建对象,缩短生命周期,减少资源占用
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(Object org) {
if (changed) {
for (Observer observer : observers) {
observer.update(this, org);
}
changed = false;
}
}
@Override
public void notifyObservers() {
notifyObservers(null);
}
// 自身的更新方法
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
// 设置更新标识,控制更新频率和状态
private void setChanged() {
changed = true;
}
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
公告板具体实现类:
public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temp;
private float humidity;
private Subject subject;
// 接收主题的消息激活
public CurrentConditionDisplay(Subject subject) {
this.subject=subject;
subject.registerObserver(this);
}
/**
* @Description: 更新信息
* @param subject:消息源主题
* @param obj:接收参数
*/
@Override
public void update(Subject subject,Object obj) {
if(subject instanceof WeatherData) {
WeatherData WeatherData = (WeatherData)subject;
this.temp=WeatherData.getTemp();
this.humidity=WeatherData.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("Current conditions: "+temp+" F degrees and "+humidity+"% humidity");
}
public void cancelRegister() {
subject.removeObserver(this);
}
}
因为公共接口的抽取,让整体的代码层次更清晰,简单;降低了代码间的耦合度,这也让未来代码扩展更方便;