观察者设计模式

来源:互联网 发布:人依赖网络例子 编辑:程序博客网 时间:2024/06/05 15:08

场景

我们接到一个来自气象局的需求:气象局需要我们构建一套系统,这系统有两个公告牌,分别用于显示当前的实时天气和未来几天的天气预报。当气象局发布新的天气数据(WeatherData)后,两个公告牌上显示的天气数据必须实时更新。气象局同时要求我们保证程序拥有足够的可扩展性,因为后期随时可能要新增新的公告牌。

概况

这套系统中主要包括三个部分:气象站(获取天气数据的物理设备)、WeatherData(追踪来自气象站的数据,并更新公告牌)、公告牌(用于展示天气数据)

WeatherStation

WeatherData知道如何跟气象站联系,以获得天气数据。当天气数据有更新时,WeatherData会更新两个公告牌用于展示新的天气数据。

错误示范

我们先来看看隔壁老王的实现:

public class WeatherData {    //实例变量声明    ...        public void measurementsChanged() {            float temperature = getTemperature();        float humidity = getHumidity();        float pressure = getPressure();        List<Float> forecastTemperatures = getForecastTemperatures();                //更新公告牌        currentConditionsDisplay.update(temperature, humidity, pressure);        forecastDisplay.update(forecastTemperatures);    }    ...}

上面这段代码是典型的针对实现编程,这会导致我们以后增加或删除公告牌时必须修改程序。我们现在来看看观察者模式,然后再回来看看如何将观察者模式应用到这个程序。

观察者模式介绍

观察者模式面向的需求是:A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的一瞬间做出反应。举个例子,新闻里喜闻乐见的警察抓小偷,警察需要在小偷伸手作案的时候实施抓捕。在这个例子里,警察是观察者、小偷是被观察者,警察需要时刻盯着小偷的一举一动,才能保证不会错过任何瞬间。程序里的观察者和这种真正的【观察】略有不同,观察者不需要时刻盯着被观察者(例如A不需要每隔1ms就检查一次B的状态),二是采用注册(Register)或者成为订阅(Subscribe)的方式告诉被观察者:我需要你的某某状态,你要在它变化时通知我。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。

观察者模式通常基于SubjectObserver接口类来设计,下面是是类图:

Observer

观察者模式的应用

结合上面的类图,我们现在将观察者模式应用到WeatherData项目中来。于是有了下面这张类图:


ObserverForWeatherStation

主题接口

/*** 主题(发布者、被观察者)*/public interface Subject {    /**     * 注册观察者     */    void registerObserver(Observer observer);    /**     * 移除观察者     */    void removeObserver(Observer observer);    /**     * 通知观察者     */    void notifyObservers(); }

观察者接口

/** * 观察者 */public interface Observer {    void update();}

公告牌用于显示的公共接口

public interface DisplayElement {    void display();}

下面我们再来看看WeatherData是如何实现的

public class WeatherData implements Subject {    private List<Observer> observers;    private float temperature;//温度    private float humidity;//湿度    private float pressure;//气压    private List<Float> forecastTemperatures;//未来几天的温度    public WeatherData() {        this.observers = new ArrayList<Observer>();    }    @Override    public void registerObserver(Observer observer) {        this.observers.add(observer);    }    @Override    public void removeObserver(Observer observer) {        this.observers.remove(observer);    }    @Override    public void notifyObservers() {        for (Observer observer : observers) {            observer.update();        }    }    public void measurementsChanged() {        notifyObservers();    }    public void setMeasurements(float temperature, float humidity,     float pressure, List<Float> forecastTemperatures) {        this.temperature = temperature;        this.humidity = humidity;        this.pressure = pressure;        this.forecastTemperatures = forecastTemperatures;        measurementsChanged();    }    public float getTemperature() {        return temperature;    }    public float getHumidity() {        return humidity;    }    public float getPressure() {        return pressure;    }    public List<Float> getForecastTemperatures() {        return forecastTemperatures;    }}

显示当前天气的公告牌CurrentConditionsDisplay

public class CurrentConditionsDisplay implements Observer, DisplayElement {    private WeatherData weatherData;    private float temperature;//温度    private float humidity;//湿度    private float pressure;//气压    public CurrentConditionsDisplay(WeatherData weatherData) {        this.weatherData = weatherData;        this.weatherData.registerObserver(this);    }    @Override    public void display() {        System.out.println("当前温度为:" + this.temperature + "℃");        System.out.println("当前湿度为:" + this.humidity);        System.out.println("当前气压为:" + this.pressure);    }    @Override    public void update() {        this.temperature = this.weatherData.getTemperature();        this.humidity = this.weatherData.getHumidity();        this.pressure = this.weatherData.getPressure();        display();    }}

显示未来几天天气的公告牌ForecastDisplay

public class ForecastDisplay implements Observer, DisplayElement {    private WeatherData weatherData;    private List<Float> forecastTemperatures;//未来几天的温度    public ForecastDisplay(WeatherData weatherData) {        this.weatherData = weatherData;        this.weatherData.registerObserver(this);    }    @Override    public void display() {        System.out.println("未来几天的气温");        int count = forecastTemperatures.size();        for (int i = 0; i < count; i++) {            System.out.println("第" + i + "天:" + forecastTemperatures.get(i) + "℃");        }    }    @Override    public void update() {        this.forecastTemperatures = this.weatherData.getForecastTemperatures();        display();    }}

到这里,我们整个气象局的WeatherData应用就改造完成了。两个公告牌CurrentConditionsDisplayForecastDisplay实现了ObserverDisplayElement接口,在他们的构造方法中会调用WeatherDataregisterObserver方法将自己注册成观察者,这样被观察者WeatherData就会持有观察者的应用,并将它们保存到一个集合中。当被观察者``WeatherData状态发送变化时就会遍历这个集合,循环调用观察者公告牌更新数据的方法。后面如果我们需要增加或者删除公告牌就只需要新增或者删除实现了ObserverDisplayElement`接口的公告牌就好了。

观察者模式将观察者和主题(被观察者)彻底解耦,主题只知道观察者实现了某一接口(也就是Observer接口)。并不需要观察者的具体类是谁、做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现了Observer接口的对象列表。

好了,我们测试下利用观察者模式重构后的程序:

public class ObserverPatternTest {    public static void main(String[] args) {        WeatherData weatherData = new WeatherData();        CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);        List<Float> forecastTemperatures = new ArrayList<Float>();        forecastTemperatures.add(22f);        forecastTemperatures.add(-1f);        forecastTemperatures.add(9f);        forecastTemperatures.add(23f);        forecastTemperatures.add(27f);        forecastTemperatures.add(30f);        forecastTemperatures.add(10f);        weatherData.setMeasurements(22f, 0.8f, 1.2f, forecastTemperatures);    }}

输出结果:

当前温度为:22.0℃当前湿度为:0.8当前气压为:1.2未来几天的气温第0天:22.0℃第1天:-1.0℃第2天:9.0℃第3天:23.0℃第4天:27.0℃第5天:30.0℃第6天:10.0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 中国人在越南办结婚证怎么办? 无线网被限速了怎么办 联通大王卡上传速度慢怎么办 小米手机下载视频速度慢怎么办 大疆御air脚架断了怎么办 大疆飞行器线断了怎么办 移动校园卡套餐到期后怎么办 流量年包到期了怎么办 家里无线网信号不好怎么办 无线网光信号红灯了怎么办 机顶盒获取不了lp地址怎么办 32内存卡丢了怎么办 手机上的相机找不到了怎么办 有刘鑫这样的闺蜜该怎么办 电脑开机网络初始化失败怎么办 电脑放音乐没有声音怎么办 苹果手机gprs信号弱怎么办 苹果导航gprs信号弱怎么办 au没有波形 没有声音怎么办 屏幕驱动板坏了怎么办 安吉星流量超了怎么办 网络被伪基站覆盖怎么办 骨头渣子卡嗓子里怎么办 执法仪记录仪关不了机怎么办 执法记录仪开不了机怎么办 华为警务通丢了怎么办 华德安执法记录仪死机怎么办 行车仪内存满了怎么办 海康威视摄像头没有通道怎么办 电脑屏膜变大了怎么办 手机2g模块坏了怎么办 腾讯大王卡是2g怎么办 华为手机4g坏了怎么办 优盘中毒打不开怎么办 vr头戴链接不起怎么办 人在缺氧的时候怎么办 脑缺氧供血不足怎么办 睡多了大脑缺氧怎么办 吃了过期的东西怎么办 吃了过期的牛肉怎么办 生存战争肉腐烂了怎么办?