• 二、观察者模式(Observer Pattern)《HeadFirst设计模式》读书笔记


    1. 我们现在要写一个气象监测的应用,它的需求如下:

        只要当天气发生变化的时候,就要更新三个布告板的显示,分别是目前天气状况(温度、湿度和气压)、气象统计(平均值等)和天气预报三个布告板。布告板要可扩展,未来可能会有新的布告板

      加入进来。

        有关天气的原始数据我们可以通过提供好的WeatherData对象获得,它提供了几个方法,getTemperature()、getHumidity()、getPressure()用于获得当前的温度、湿度和气压,还有一个 

      measurementsChanged()方法,一旦气象数据更新时,此方法就会自动被调用。 

                                       

      2. 我们可以通过在measurementsChanged()方法内写入布告板显示的代码,比如先加入一个目前天气状况的布告板显示更新,这样天气发生变化时,随着measurementsChanged方法被调用,我们的代

    码就执行了,布告板也就更新了:

    public void measurementsChanged(){ 
        float temp = getTemperature(); 
        float humidity= getHumidity(); 
        float pressure= getPressure(); 
      //目前天气状况的布告板显示更新 
      currentConditionssDisplay.update(temp,humidity,pressure); 
    }

      但是这样代码耦合在一起了,如果以后需要加入新的布告板,只能修改当前代码。因此我们就可以采用观察者模式。

      3. 观察者模式:

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

      可以理解为微博的关注,一个微博大V可以被很多人关注,当他发动态时就会通知到所有的粉丝。这里的微博大V就是一个主题,而关注他的粉丝就是观察者,当主题变化的时候,观察者会收到通知并执

    行下一步动作。当然观察者同时也可以是一个主题,反之亦然。

      在上面气象监测的应用中,我们也可以采用观察者模式,将WeatherData当成一个主题,将布告板当成观察者,让布告板订阅WeatherData这个主题,这样一旦天气发生变化,所有订阅了的布告板就都会

    收到通知,就可以执行更新操作了。通过观察者模式我们可以实现将观察者和主题解耦,观察者可以随时订阅和取消订阅对应的主题,而不用影响主题。

       上面说了观察者模式的适用场景和好处,具体的实现可以参照下图:

      

      因此我们可以让WeatherData实现主题接口,让三个布告板实现观察者接口,并通过主题提供的注册方法让观察者都订阅WeatherData。

      首先新建一个主题接口:

    public interface Subject {
        void registerObserver(Observer observer);
        void removeObserver(Observer observer);
        void notifyObserver();
    }

      新建观察者接口:

    public interface Observer {
        void update(float temp,float humidity,float pressure);
    }

      新建一个显示接口用于布告板的显示:

    public interface DisplayElement {
        //当布告板需要显示时,实现此方法
        void display();
    }

      WeatherData实现Subject接口,放一个一个ArrayList类来存放观察者:

    public class WeatherData implements Subject {
        //定义温度、适度和气压
        private float temp;
        private float humidity;
        private float pressure;
    
        //创建一个ArrayList用于存放订阅此主题的观察者,通过构造方法初始化
        private ArrayList<Observer> observers;
    
        public WeatherData(){
            observers = new ArrayList<>();
        }
    
        //将观察者注册到主题,即添加到ArrayList中
        @Override
        public void registerObserver(Observer observer) {
            observers.add(observer);
        }
    
        //将观察者移除,即从ArrayList中remove
        @Override
        public void removeObserver(Observer observer) {
            //先判断下observer是否存在,不存在会返回-1
            int i = observers.indexOf(observer);
            if (i >= 0) {
                observers.remove(i);
            }
        }
    
        //通知所有观察者,即调用所有观察和的update()方法,,将更新的温度、湿度、气压传进去
        @Override
        public void notifyObserver() {
            for (Observer observer : observers) {
                observer.update(temp, humidity, pressure);
            }
        }
    
        //天气变化时自动调用的方法
        public void measurementsChanged(){
            notifyObserver();
        }
    
        //模拟自动获得气象参数更新的方法,通过set方法更新参数,并调用measurementsChanged()方法
        public void setParameters(float temp,float humidity,float pressure){
            this.temp = temp;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
    }

      新建一个显示当前天气状况的布告板CurrentConditionsDisplay ,实现Observer接口,另外实现DisplayElement接口,重写display()方法显示布告板内容。

    public class CurrentConditionsDisplay implements Observer,DisplayElement{
        //定义温度、湿度和气压
        private float temp;
        private float humidity;
        //保存主题是为了让以后移除观察者时更加方便
        private WeatherData weatherData;
    
        //在构造方法中直接完成观察者对主题的注册
        public CurrentConditionsDisplay(WeatherData weatherData) {
            this.weatherData = weatherData;
            weatherData.registerObserver(this);
        }
    
        @Override
        public void update(float temp, float humidity, float pressure) {
            this.temp = temp;
            this.humidity = humidity;
            display();
        }
    
        //显示当时的温度和气压
        @Override
        public void display() {
            System.out.println("Current Conditions:" + temp + "F degrees and" + humidity + "% humidity");
        }
    }

      上面在构造方法中注册了主题,并保存了WeatherData对象,书中说是为了以后移除观察者的时候方便。

      下面测试一下:

    public class Test02 {
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            //新建布告板,构造方法中会将观察者注册到weatherData
            CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
            //模拟天气状况发生改变
    weatherData.setParameters(80,65,30.4f); weatherData.setParameters(78,63,29.4f); weatherData.setParameters(76,61,28.4f); } }

      输出结果:

       

       可以看到,每次天气发生变化,主题都会通知到观察者做出更新。当我们有新的观察者时,只需要实现Observer并注册到WeatherData,就会自动接到通知了。

      4. 其实Java内置了观察者模式,在上面的例子中,我们发现,观察者只能被动的接受通知,不能主动获取主题的状态。而在Java内置的Observer模式既支持被动接收,也支持主动拉取。

      Java内置的观察者模式为java.util包下的Observable类和Observer接口,Observable类就相当于一个主题,可被观察的,Observer就是观察者。不同之处在于Observable是一个类而不是接口,而且有

    一个changed标志位,用来标志当前状态是否改变,如果为true,就通知管观察者,并提供了setChanged()来将changed置为true,可通过clearChanged()清除改变状态(将changed置为false,hasChanged()

    返回changed的当前值。通过changed标志可以更灵活的控制通知,比如想在某种情况下再通知而不是已有改变就进行通知。

      此外Observable中同样提供了添加、删除和通知Observer的方法。Observer接口则只有一个update(Observable o, Object arg)方法,参数中Observable 用来表示是哪个主题,Object为传递的数据对象。

      除此之外,Observable类中的notifyObservers()方法是倒序通知观察者的,因此在使用观察者模式的时候不能太依赖于观察者被通知的次序,如果这样的话,一旦实现方式有所改变,通知次序就会改变,

    耦合度就变高了。

      5. 总结:

        1)观察者模式适用于一对多的关系中,一个对象的状态发生改变,可以通知到多个依赖它的对象进行更新。

        2)java自带的观察者模式的主题即可观察者Observable是一个类而不是接口,限制了它的使用和复用,因为类是单继承而接口可以多继承。因此如果有需要的话我们可以自己写主题接口。

      

      

           

  • 相关阅读:
    Google Protocol Buffer 的使用和原理(转)
    在python开发工具PyCharm中搭建QtPy环境(详细)
    Docker容器的操作
    Docker镜像操作
    最新版本Docker的安装和使用
    linux CentOS如何安装KVM
    在Linux CentOS下如何安装tar.gz和RPM软件包
    Linux忘记root密码后如何在grub界面中以单用户模式进入系统并重置密码的方法
    Django中的Project和App的区别
    Python处理PDF和Word文档常用的方法(二)
  • 原文地址:https://www.cnblogs.com/advancedcz/p/13096469.html
Copyright © 2020-2023  润新知