天气预报需求
具体要求如下:
- 1)气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)
- 2)需要设计开放型 API,便于其他第三方也能接入气象站获取数据
- 3)提供温度、气压和湿度的接口
- 4)测量数据更新时,要能实时的通知给第三方
天气预报需求方案之普通方案
WeatherData类
通过对气象站项目的分析,我们可以初步设计出一个WeatherData
类
- 1)通过
getXxx
方法,可以让第三方接入,并得到相关信息
- 2)当数据有更新时,气象站通过调用
dataChange()
去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送
CurrentConditions
(当前的天气情况)可以理解成是我们气象局的网站
核心代码
气象网站类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class CurrentConditions { private Float temperature; private Float pressure; private Float humidity;
public void update(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); }
public void display() { System.out.println("============最新天气============"); System.out.println("*** 当前温度:" + this.temperature + " ℃ ***"); System.out.println("*** 当前气压:" + this.pressure + " kPa ***"); System.out.println("*** 当前湿度:" + this.humidity + " %RH ***"); } }
|
气象数据类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
public class WeatherData { private Float temperature; private Float pressure; private Float humidity; private CurrentConditions conditions;
public WeatherData(CurrentConditions conditions) { this.conditions = conditions; }
public Float getTemperature() { return temperature; }
public Float getPressure() { return pressure; }
public Float getHumidity() { return humidity; }
public void dataChange() { conditions.update(getTemperature(), getPressure(), getHumidity()); }
public void setData(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; dataChange(); } }
|
客户端调用
1 2 3 4 5 6 7 8
| CurrentConditions currentConditions = new CurrentConditions();
WeatherData weatherData = new WeatherData(currentConditions);
weatherData.setData(10f, 150f, 40f);
|
测试结果
1 2 3 4 5 6 7 8 9 10 11 12
| ============最新天气============ *** 当前温度:10.0 ℃ *** *** 当前气压:150.0 kPa *** *** 当前湿度:40.0 %RH *** ============最新天气============ *** 当前温度:15.0 ℃ *** *** 当前气压:130.0 kPa *** *** 当前湿度:60.0 %RH *** ============最新天气============ *** 当前温度:13.0 ℃ *** *** 当前气压:160.0 kPa *** *** 当前湿度:20.0 %RH ***
|
问题分析
- 1)其他第三方接入气象站获取数据的问题
- 2)无法在运行时动态的添加第三方(新浪网站)
- 3)违反
OCP
原则 => 观察者模式
在WeatherData
中增加第三方时,都需要创建对应的第三方公台板对象并加入到dataChange()
方法中,既不是动态加入,也不利于维护
观察者模式原理
观察者模式类似订牛奶业务
- 1)奶站 / 气象局:
Subject
- 2)用户 / 第三方网站:
Observer
Subject
:登记注册、移除和通知
- 1)
registerObserver()
:注册
- 2)
removeObserver()
:移除
- 3)
notifyObservers()
:通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
Observer
:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject
,依赖的对象为Observer
,Subject
通知Observer
变化,比如这里的奶站是Subject
,是1的一方。用户是Observer
,是多的一方
天气预报需求方案之观察者模式
UML 类图
核心代码
观察者对象Observer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public interface Observer { void update(Float temperature, Float pressure, Float humidity); }
public class CurrentConditions implements Observer { private Float temperature; private Float pressure; private Float humidity;
@Override public void update(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); }
public void display() { System.out.println("============最新天气============"); System.out.println("*** 当前温度:" + this.temperature + " ℃ ***"); System.out.println("*** 当前气压:" + this.pressure + " kPa ***"); System.out.println("*** 当前湿度:" + this.humidity + " %RH ***"); } }
|
主体对象Subject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
public interface Subject { void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers(); }
public class WeatherData implements Subject { private Float temperature; private Float pressure; private Float humidity; private List<Observer> observerList;
public WeatherData() { observerList = new ArrayList<>(); }
public Float getTemperature() { return temperature; }
public Float getPressure() { return pressure; }
public Float getHumidity() { return humidity; }
public void dataChange() { notifyObservers(); }
public void setData(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; dataChange(); }
@Override public void registerObserver(Observer o) { observerList.add(o); }
@Override public void removeObserver(Observer o) { if(o!= null && observerList.contains(o)) { observerList.remove(o); } }
@Override public void notifyObservers() { for (Observer observer : observerList) { observer.update(temperature, pressure, humidity); } } }
|
观察者对象Observer
1 2 3
| public interface Observer { void update(Float temperature, Float pressure, Float humidity); }
|
调用测试
1 2 3 4 5 6 7 8 9 10 11 12
| CurrentConditions currentConditions = new CurrentConditions();
WeatherData weatherData = new WeatherData();
weatherData.registerObserver(currentConditions);
weatherData.setData(10f, 150f, 40f);
|
观察者模式的好处
- 1)观察者模式设计后,会以集合的方式来管理用户
Observer
,包括注册、移除和通知
- 2)这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类
WeatherData
不会修改代码,遵守了ocp
原则
例如,我们新增SinaWebSite
和BaiDuWebSite
两个三方网站,接口气象局。此时三方只需实现相应接口即可,WeatherData
不需要有任何的改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
public class SinaWebSite implements Observer { private Float temperature; private Float pressure; private Float humidity;
@Override public void update(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); }
public void display() { System.out.println("============新浪网-最新天气============"); System.out.println("*** 新浪网-当前温度:" + this.temperature + " ℃ ***"); System.out.println("*** 新浪网-当前气压:" + this.pressure + " kPa ***"); System.out.println("*** 新浪网-当前湿度:" + this.humidity + " %RH ***"); } }
public class BaiDuWebSite implements Observer { private Float temperature; private Float pressure; private Float humidity;
@Override public void update(Float temperature, Float pressure, Float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); }
public void display() { System.out.println("============百度网-最新天气============"); System.out.println("*** 百度网-当前温度:" + this.temperature + " ℃ ***"); System.out.println("*** 百度网-当前气压:" + this.pressure + " kPa ***"); System.out.println("*** 百度网-当前湿度:" + this.humidity + " %RH ***"); } }
|
调用测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| weatherData.registerObserver(new SinaWebSite()); weatherData.registerObserver(new BaiDuWebSite());
weatherData.setData(15f, 120f, 80f);
|
当三方网站不再需要时,只要做相应的移除即可
1 2 3 4 5 6 7 8 9 10 11
| weatherData.removeObserver(currentConditions); weatherData.setData(20f, 160f, 30f);
|
JDK 源码分析
JDK 中的Observable
就使用到了观察者模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class Observable { private boolean changed = false; private Vector<Observer> obs = new Vector();
public Observable() { }
public synchronized void addObserver(Observer var1) { if (var1 == null) { throw new NullPointerException(); } else { if (!this.obs.contains(var1)) { this.obs.addElement(var1); }
} }
public synchronized void deleteObserver(Observer var1) { this.obs.removeElement(var1); }
public void notifyObservers() { this.notifyObservers((Object)null); } }
public interface Observer { void update(Observable o, Object arg); }
|
角色分析和职责说明
- 1)
Observable
即充当了Subject
接口及其实现类,其中包括了Observer
的集合,并且包括对观察者相关的注册、移除和通知等方法:addObserver()
、deleteObserver()
和notifyObservers()
,这些方法就类似于我们上面例子中的registerObserver()
、removeObserver()
和notifyObservers()
- 2)
Observer
即观察者接口,具有update()
方法