zoukankan      html  css  js  c++  java
  • 《Head First 设计模式》之观察者模式

    作者:Grey

    原文地址:http://www.cnblogs.com/greyzeng/p/5918205.html

    模式名称

    观察者模式(Observer Pattern)

    需求

    我们要通过WeatherData对象从物理气象站获取温度,湿度,气压数据,并且更新三个布告板的信息:

    • 目前状况(温度,湿度,气压)
    • 气象统计
    • 天气预报

    解决方案

    WeatherData类中,我们可以通过以下三个方法来获取测量值:

    getTemperature() // 获取温度
    getHumidity() // 获取湿度
    getPressure() // 获取气压
    

    当新的测量数据准备好以后,

    measurementsChanged()
    

    方法被调用

    然后我们就可以更新三个布告板的数据了。

    更多地,我们需要满足可扩展的要求,可以方便添加/删除布告板

    一种实现:

    public class WeatherData {
        public void measurementsChanged() {
            float temperature = getTemperature(); // 获取温度
            float humidity =  getHumidity() // 获取湿度
            float pressure = getPressure() // 获取气压
            currentConditionsDisplay.update(temperature, humidity, pressure); // 更新“目前状况”布告板
            statisticsDisplay.update(temperature, humidity, pressure); // 更新“气象统计”布告板
            forecastDisplay.update(temperature, humidity, pressure); // 更新“天气预报”布告板
        }
    }
    

    这个模型类似于出版者和订阅者的关系,出版者发布了主题(气压,湿度,温度),订阅者订阅这些主题,等这些主题有新的内容的时候,会自动推送给订阅了这个主题的订阅者这里,这就是观察者模式,其中出版者被称为“主题”(Subject),订阅者被称为“观察者”(Observer)。

    public interface Observer {
        // 观察者根据传入的参数进行更新操作。
        public void update(temperature, humidity, pressure); 
    }
    
    public interface Subject {
        void registerObserver(Observer o); // 注册观察者
        void removeObserver(Observer o); // 移除观察者
        void notifyObservers(); // 主题变化后,通知观察者
    }
    

    WeatherData需要实现主题接口

    public class WeatherData implements Subject {
        private ArrayList<Observer> observers; // 用来装需要订阅这个主题的观察者
        private float temperature;
        private float humidity;
        private float pressure;
        WeatherData() {
            observers = new ArrayList<>();
        }
        @Override
        public void registerObserver(Observer o) {
            observers.add(o); // 订阅
    
        }
    
        @Override
        public void removeObserver(Observer o) {
            if (null == observers || 0 == observers.size()) {
                System.out.println("observers is null");
            } else {
                int i = observers.indexOf(o);
                if (i >= 0) {
                    observers.remove(i); // 取消订阅
                }
            }
        }
    
        @Override
        public void notifyObservers() {
            if (null == observers || 0 == observers.size()) {
                System.out.println("observers is null");
            } else {
                for (Observer observer : observers) {
                    observer.update(temperature, humidity, pressure); // 更新每个观察者的状态
                }
            }
        }
    
        
        public void setMeasurements(float temperature, float humidity, float pressure) {
            // 当我们调用setMeasurements()方法的时候,就获取最新的数据并通知每个观察者。
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            notifyObservers();
        }
    
    }
    

    每个布告板作为观察者,需要实现Observer接口,以“当前状态”布告板为例:

    public class CurrentConditionsDisplay implements Observer{
        private float temperature;
        private float humidity;
        CurrentConditionsDisplay(Subject weatherData) {
            weatherData.registerObserver(this);
        }
        @Override
        public void update(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            System.out.println("update CurrentConditionDisplay,humidity:"+humidity+" temperature:"+temperature);
        }
    }
    

    类似地,我们可以得到其余布告板,比如天气预报布告板:ForecastDisplay,

    public class ForecastDisplay implements Observer{
        private float temperature;
        private float humidity;
        private float pressure;
        ForecastDisplay(Subject weatherData) {
            weatherData.registerObserver(this);
        }
        @Override
        public void update(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            System.out.println("update CurrentConditionDisplay,humidity:" + humidity + " temperature:" + temperature + " pressure:" + pressure);
        }
    }
    

    测试代码:

    public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            new CurrentConditionsDisplay(weatherData);
            weatherData.setMeasurements(83,3,33);
            weatherData.setMeasurements(23,45,48);
            weatherData.setMeasurements(4,6,8);
    
            new ForecastDisplay(weatherData);
            weatherData.setMeasurements(1,2,3);
        }
    }
    

    输出结果:

    update CurrentConditionDisplay,humidity:3.0 temperature:83.0
    update CurrentConditionDisplay,humidity:45.0 temperature:23.0
    update CurrentConditionDisplay,humidity:6.0 temperature:4.0
    update CurrentConditionDisplay,humidity:2.0 temperature:1.0
    update CurrentConditionDisplay,humidity:2.0 temperature:1.0 pressure:3.0
    

    JDK中内置的观察者模式

    java.util.Observable;
    java.util.Observer;
    

    这两个类和我们之前讨论的SubjectObserver类似,

    java.util.Observable中提供了:

    setChanged(); // 表明状态已经改变
    notifyObservers(); // 通知所有观察者
    addObserver(Observer o); // 增加观察者
    

    java.util.Observer中提供了:

    update(); // 更新订阅信息
    

    采用JDK内置的观察者模式修改后的WeatherData

    public class WeatherData extends Observable {
        private float temperature;
        private float humidity;
        private float pressure;
        WeatherData() {
        }
    
    
        private void measurementsChanged() {
            setChanged();
            notifyObservers();
        }
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
        float getTemperature() {
            return temperature;
        }
        float getHumidity() {
            return humidity;
        }
        float getPressure() {
            return pressure;
        }
    
    }
    

    CurrentConditionsDisplay

    public class CurrentConditionsDisplay implements Observer{
        private float temperature;
        private float humidity;
        CurrentConditionsDisplay(Observable observable) {
            observable.addObserver(this);
        }
    
    
        @Override
        public void update(Observable o, Object arg) {
            if (o instanceof WeatherData) {
                WeatherData weatherData = (WeatherData)o;
                this.temperature = weatherData.getTemperature();
                this.humidity = weatherData.getHumidity();
                System.out.println("update CurrentConditionDisplay,humidity:"+humidity+" temperature:"+temperature);
            }
        }
    }
    

    测试代码:

    public class WeatherStation {
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            new CurrentConditionsDisplay(weatherData);
            weatherData.setMeasurements(83,3,33);
            weatherData.setMeasurements(23,45,48);
            weatherData.setMeasurements(4,6,8);
        }
    }
    
    

    输出结果:

    update CurrentConditionDisplay,humidity:3.0 temperature:83.0
    update CurrentConditionDisplay,humidity:45.0 temperature:23.0
    update CurrentConditionDisplay,humidity:6.0 temperature:4.0
    
    

    完整代码: Observer Pattern Source

  • 相关阅读:
    百度&高德地图小区景点边界轮廓实现
    使用Hexo & Github,搭建属于自己的博客
    网页背景图固定不动,不跟随滚动条滚动
    jQuery同步Ajax带来的UI线程阻塞问题及解决方法
    Java并发编程实战(5)- 线程生命周期
    Java并发编程实战(4)- 死锁
    Java并发编程实战(3)- 互斥锁
    Java并发编程实战(2)- Java内存模型
    Java并发编程实战(1)- 并发程序的bug源头
    软件测试待解决问题
  • 原文地址:https://www.cnblogs.com/greyzeng/p/5918205.html
Copyright © 2011-2022 走看看