设计模式之观察者模式

客户需求

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

程序设计

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

基本概念

  • 定义

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

  • 角色

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

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

    • 抽象观察者

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

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

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

    • 具体的观察者

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

  • UML图

设计模式之观察者模式_第1张图片
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 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图
设计模式之观察者模式_第2张图片
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 设计模式

你可能感兴趣的:(设计模式之观察者模式)