Head First设计模式(阅读笔记)-02.观察者模式
创始人
2024-02-06 06:10:07

气象监测应用

建立一个应用,利用WeatherData对象取得气象站的数据,并更新三个布告板:目前状况、气象统计和天气预报


要求


  • WeatherData类具有getter方法获取温度、湿度和气压
  • 获取到新的数据时会调用measurementsChanged方法
  • 当有新数据时三个布告板需要更新
  • 系统需要具有扩展性,比如添加新的布告板

简单实现

未使用设计模式的代码如下,存在许多问题:

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);forcecastDisplay.update(temp, humidity, pressure);}// 其他方法
}

  • 针对具体实现编程,意味着在后续增加或删除布告板时需要修改程序
  • 没有封装改变的部分,比如更新布告板的实现代码

观察者模式


从订阅报纸入手

先了解下报纸的订阅/取消订阅过程:

  • 当你向某家报社订阅报纸后,每次它们出版新报纸时就会送一份给你
  • 如果你不想在看报纸就可以取消订阅,报社也就不再送了

在观察者模式中,报纸这样的出版者称为主题,我们这样的订阅者称为观察者:

  • 主题对象管理数据,当主题内的数据改变时就会通知观察者
  • 观察者订阅(注册)主题后,每当主题数据改变时能收到更新
  • 同时每个观察者也可以把自己从该主题的观察者集合中删除

定义观察者模式

观察者模式定义了对象之间一对多的依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新


在这里插入图片描述

观察者模式的优势

让主题和观察者之间松耦合


  • 主题只知道观察者实现了Observer接口,它并不需要知道具体观察者是谁以及做了什么
  • 主题唯一依赖的东西是一个实现Observer接口的对象列表,所以任何时候都可以新增/删除观察者
  • 有新的观察者出现时,主题的代码也无需修改,新的类只需要实现观察者接口并且注册为观察者即可
  • 可以单独复用主题或观察者,因为它们非紧耦合

重新设计应用


实现气象站


// 主题接口
public interface Subject{public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyObservers();
}// 观察者接口
public interface Observer{// 这些数值发生变化,主题就会把新数值作为参数传递给观察者public void update(float temp, float humidity, float pressure);
}// 展示内容接口
public interface DisplayElment{public void display();  // 布告板需要显示时就调用该方法
}

在WeatherData中实现主题接口


// 具体的主题类WeatherData
public class WeatherData implements Subject{private ArrayList observers;  // 记录观察者,在构造器中初始化private float temperature;private float humidity;private float pressure;public WeatherData(){observers = new ArrayList();}public void registerObserver(Observer o){observers.add(o);  // 观察者的注册即加入到观察者列表中即可}public void removeObserver(Observer o){int i = observers.indexOf(o);if(i >= 0){Observers.remove(i);  // 观察者的删除即从列表中移除即可}}public void notifyObservers(){for(int i = 0; i < observers.size(); i++){Observer Observer = (Observer)observers.get(i);// 调用列表中每个Observer的update方法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 class CurrentConditionsDisplay implements Observer, DisplayElement{private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData){// 目前状况布告板需要把自己注册给weatherDatathis.weatherData = weatherData;weatherData.registerObserver(this);}public void update(float temperature, float humidity, float pressure){// 目前状况布告板只需要temperature和humiditythis.temperature = temperature;this.humidity = humidity;display();}public void display(){System.out.println("目前状况: " + temperature + "度 " + humidity);}
}

启动


public class WeatherStation{public static void main(String[] args){WeatherData weatherData = new WeatherData();CurrentConditionsDisplay c = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(10,10,10.3f);}
}

Java内置的观察者模式

在上述操作中每次都是主题去送所有数据给注册好的观察者们,但是并不是每个观察者需要所有数据,为什么不能让观察者自己选择取需要的数据呢?Java API中内置的观察者模式就实现了两种方式传递数据


在WeatherData中继承Observable


import java.util.Observable;
public class WeatherData extends Observable{private float temperature;private float humidity;private Subject weatherData;public WeatherData(){} // 因为这里实现拉取方式,WeatherData也就不需要记录观察者们// 使用推方式的时候需要使用setMeasurements和measurementsChangedpublic void measurementsChanged(){// 该方法用于标记状态状态已经改变,让notifyObservers方法知道该方法被调用时应该更新观察者// 这样就不会出现每次更新一点数据就通知观察者,可以设置每次更新到某个阈值再去调用setChanged方法setChanged();  notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure){this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}// getter用于拉方式的时候使用public float getTemperature(){return temperature;}public float getHumidity(){return humidity;}public float getPressure(){return pressure;}
}

重新建立布告板


import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement{Observable Observable;private float temperature;private float humidity;public CurrentConditionsDisplay(Observable observable){// 目前状况布告板需要把自己注册给weatherDatathis.observable = observable;observable.addObserver(this);}// 主题作为第一个变量,好让观察者知道是哪个主题通知它public void update(Observable obs, Object arg){if(obs instanceof WeatherData){WeatherData weatherData = (WeatherData)obs;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}public void display(){System.out.println("目前状况: " + temperature + "度 " + humidity);}
}

缺点

java.util.ObservableJDK9之后已被标记为过时类


  • java.util.Observable是一个类并非接口,由于Java的单继承机制,所以限制了它的复用
  • setChanged方法被protected修饰,所以要创建Observable实例并组合到自己对象中就必须继承Observable(不是很明白),违法了多用组合,少用继承的设计原则

参考

Head First 设计模式-观察者模式

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...