设计模式之观察者模式

作者: BlainPeng | 来源:发表于2016-04-14 20:04 被阅读440次

    客户需求

    /**
     * 需求: 
     * 
     * 气象站提供最新的天气预报信息,包括: 温度、湿度、气压,这些信息一发生变化, 客户端的信息需要同时更新 请用代码实现模拟实现该功能
     * 
     * 
     */
    

    程序设计

    一个气象站对应着多个客户端,气象站的数据一发生变化,客户端的数据也要随着更新,这就形成了一种依赖关系,并且是一对多的关系,很显然,我们可以用观察者模式来完成此功能

    基本概念

    • 定义

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

    • 角色

      • 抽象被观察者(或者叫主题)

      它是一个接口或者抽象类,用来把所有观察者对象的引用保存到一个集合中。定义三个方法:注册观察者、解除观察者、通知观察者;这三个方法就是被观察者与观察者通信的桥梁

      • 抽象观察者

      为所有的具体的观察者定义的一个接口,定义一个方法:更新;用于获取被观察者发生变化时更新自己

      • 具体的被观察者(或者叫主题)

      在被观察者内部状态发生变化,通知所有注册过的的观察者。通常实现或继承抽象被观察者

      • 具体的观察者

      实现抽象观察者接口,使自己与具体的被观察者的状态保持一致

    • UML图

    ObserverPattern.png
    • DemoOne

      • 被观察者

          public interface Subject
          {
              public void registerObserver(Observer observer);
          
              public void unregisterObserver(Observer observer);
          
              public void notifyObservers();
          }
        
      • 观察者

          public interface Observer
          {
              public void update(float temp, float humidity, float pressure);
          }
        
      • 具体被观察者

          public class WeatherDataSubject implements Subject
          {
              private float               temperature;
              private float               humidity;
              private float               pressure;
              /**
               * 记录所有的观察者
               */
              private ArrayList<Observer> observers;
          
              public WeatherDataSubject() {
                  observers = new ArrayList<>();
              }
          
              @Override
              public void registerObserver(Observer observer)
              {
                  observers.add(observer);
              }
          
              @Override
              public void unregisterObserver(Observer observer)
              {
                  int indexOf = observers.indexOf(observer);
                  if (indexOf >= 0)
                  {
                      observers.remove(indexOf);
                  }
              }
          
              @Override
              public void notifyObservers()
              {
                  for (int i = 0; i < observers.size(); i++)
                  {
                      Observer observer = observers.get(i);
                      observer.update(temperature, humidity, pressure);
                  }
              }
          
              public void setMeasurements(float temperature, float humidity, float pressure)
              {
                  this.temperature = temperature;
                  this.humidity = humidity;
                  this.pressure = pressure;
                  notifyObservers();
              }
          
          }
        
      • 具体观察者

          public class CurrentConditionsDidsplay implements Observer
          {
              @Override
              public void update(float temp, float humidity, float pressure)
              {
                  System.out.println("temperature=" + temp + "\t humidity=" + humidity + "\t pressure=" + pressure);
              }
          }
        
      • 测试

          public class ObserverPatternTest
          {
              public static void main(String[] args)
              {
                  WeatherDataSubject subject = new WeatherDataSubject();
                  CurrentConditionsDidsplay condition = new CurrentConditionsDidsplay();
                  subject.registerObserver(condition);
                  subject.setMeasurements(23.0f, 1.0f, 350.0f);
              }
          }
        

    以上这种方式只是简单的实现了被观察者数据发生变化时,主动将数据送过来,是一种推的模式。它不会管观察者到底需不需要全部的数据。但是,有的观察者可能只需要一点点数据,不想收到一堆数据,那此时怎么办呢?那被观察者是否可以提供一个get方法让观察者自己去获取数据,仅通知数据有变化了?接下来我们看看Java内置的观察者模式

    • UML图
    Java_ObserverPattern.png
    • DemoTwo
      • 具体的被观察者

        public class WeatherDataObservable extends Observable
        {
            private float   temperature;
            private float   humidity;
            private float   pressure;
        
            public void setMeasurements(float temperature, float humidity, float pressure)
            {
                this.temperature = temperature;
                this.humidity = humidity;
                this.pressure = pressure;
                //仅仅是用来通知观察者我的状态发生变化了
                setChanged();
                notifyObservers();
            }
        
            public float getTemperature()
            {
                return temperature;
            }
        
            public float getHumidity()
            {
                return humidity;
            }
        
            public float getPressure()
            {
                return pressure;
            }
        }
        
      • 具体的观察者

        public class CurrentConditionsDidsplayObserver implements Observer
        {
        
            @Override
            public void update(Observable observable, Object obj)
            {
                if (observable instanceof WeatherDataObservable)
                {
                    WeatherDataObservable data = (WeatherDataObservable) observable;
                    float temperature = data.getTemperature();
                    float humidity = data.getHumidity();
                    float pressure = data.getPressure();
                    System.out.println("temperature=" + temperature + "\t humidity=" + humidity + "\t pressure=" + pressure);
                }
            }
        }
        

    java内置的观察者模式是属于拉数据模式,被观察者状态发生变化了,通过setChanged方法仅通知观察者状态发生了变化,拉不拉数据是观察者的事了,与被观察者无关了。但是这种设计方式也会有一定的限制:

    (1)Observable是一个类,所以我们的子类需继承它,若此时被观察者需同时继承另一个超类,就会陷入两难了,毕竟Java不支持多重继承,这限制了Observable的复用能力

    (2)Observable中的setChanged方法被protected了,这意味着:除非你继承Observable类,否则你无法创建Observable实例并组合到你自己的对象中来。“多用组合,少用继承”

    知识总结

    这两种模式的使用,取决于系统设计时的需要。如果观察者比较复杂,并且观察者进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果观察者比较简单,则“拉模式”就比较适合。

    参考资料

    Head First 设计模式

    相关文章

      网友评论

      本文标题:设计模式之观察者模式

      本文链接:https://www.haomeiwen.com/subject/usnglttx.html