需求分析
气象站提供一个WeatgerData类,里面有三个get方法,分别可以取到三个值:温度,湿度,气压。
我们需要实现三个布告板,分别是 “目前状态布告板”,“气象统计布告板”,“天气预告布告板。”
要求:
1、三个布告板都会用到气象站提供的数据。
2、并且一旦气象站数据发生变化,布告板上的数据也必须跟着变化。
3、布告板可以随心所欲的删除,或者添加新的布告板。
第一次设计:最“简单”的设计
public class WeatherData { //获取温度接口 public float getTemperature(){return 0;} //获取湿度接口 public float getHumidity(){return 0;} //获取气压接口 public float getPressure(){return 0;} //一旦测量数据变化就调用,并且更新三个布告板 public void measurementsChanged(){ float temperature = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); //调用更新三个布告板 } }
很简单。但是如再新增一个布告板呢?WeatherData就要被修改一次。
现在目的要让布告板与气象站松耦合,并且想象一下假如有上百个布告板,我其中有一个不需要气象站的数据怎么办?
目前气象站是在强行喂饭给布告板啊。
分析
假如weatherData(气象站)是一个出版社,而布告板是订阅该出版社的人。
当出版社每次更新数据时,订阅者就可以收到出版社数据。 并且订阅者有权决定是否允许你给我传递数据。
问题在于,如何让气象站知道哪个布告板订阅了自己。
分析、抽象变动接口
为了解决以上问题,我们在气象站类中采用一个集合来记录订阅者。那么订阅者通过注册的途径来放入集合中。
根据策略模式,抽象出一个主题接口,专门用来注册订阅者,删除订阅者,修改订阅者(传递改变后的数据给订阅者)
public interface Subject { //注册观察者 public void registerObserver(Observer observer); //删除观察者 public void removeObserver(Observer observer); //主题状态改变时,通知所有观察者 public void notifyObservers(); }
抽象出一个数据类,专门用来存放气象站的数据
public class NewData { float temperature; float humidity; float pressure; }
抽象出一个订阅者超类,称为观察者。 当气象站的数据改变时,实现该接口的订阅者便会调用update()
public interface Observer { public void update(NewData newData); }
抽象出一个布告板展示接口
public interface DisplayElement { public void display(); }
第二次设计
气象站实现了主题接口。并且实现了三个方法,注册观察者(订阅者)、删除、以及更新数据。
不要迷惑的地方:
setNewData(NewData newData); 这个方法是气象站数据改变的时候调用的,什么时候改变是气象站决定的,我们不设计,我们只给气象站提供一个外部访问接口。就是它们改变的时候需调用到我们这个接口方法。
public class WeatherData implements Subject { private List<Observer> observers; private NewData newData; public WeatherData(){ //初始化记录集合 observers = new ArrayList(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int i = observers.indexOf(observer); if(i > 0){ observers.remove(i); } } @Override public void notifyObservers() { for(int i = 0; i < observers.size(); i ++){ Observer observer = observers.get(i); observer.update(newData); } } public void measurementsChanged(){ notifyObservers(); } public void setNewData(NewData newData){ this.newData = newData; measurementsChanged(); } }
布告板实现
布告板实现了观察者接口,展示布告板接口。
刚才说过了,布告板就是订阅气象站数据的订阅者,观察者是订阅者抽象出来的接口。订阅者需要去注册,来表明自己需要气象站的数据。
public class CurrentConditionsDisplay implements Observer, DisplayElement { private NewData newData; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void display() { System.out.println("目前布告板数据是:"+newData); } @Override public void update(NewData newData) { this.newData = newData; } }
测试
public class Test { public static void main(String[] args) { //声明一个气象站 WeatherData weatherData = new WeatherData(); //声明一个布告板,并且传入气象站,进行注册 CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); //新的数据 NewData newData = new NewData();
//一定newData对象变化,就设置新数据。 weatherData.setNewData(newData); } }
小节
会发现假如新增一个布告板,我只需要再new一个就好了嘛。 不需要再去进入weatherData类中进行修改了。
目前观察者(订阅者)还是被出版者(气象站) 强行喂饭。 凡是在气象站中注册了的布告板 都会被强行刷新数据。
定义观察者模式
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
可以理解为:一个出版社对应多个订阅者。