最近看了<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); } }
因为公共接口的抽取,让整体的代码层次更清晰,简单;降低了代码间的耦合度,这也让未来代码扩展更方便;