观察者(Observer)模式

观察者(Observer)模式例子

这里引入例子,当前有几个硬件设备,负责收集当前温度,湿度和气压,WeatherData对象从气象站收集数据,然后WeatherData向显示器发送数据,对应显示面板解析数据后显示(计算原始数据,输出日常所见的数据)
观察者(Observer)模式_第1张图片
由于各种场合的需要,有的人只需要看到气象状况,有的人需要看到天气预报,所有根据需要显示不同信息,显示到不同的面板
观察者(Observer)模式_第2张图片

初期设计
观察者(Observer)模式_第3张图片

对应代码实现
DisplayElement

package headfirst.hd.observer.old.interfaces;

public interface DisplayElement {
    void display(float temperature, float humidity, float pressure);
}

CurrentConditionsDisplay

package headfirst.hd.observer.old.impl;

import headfirst.hd.observer.old.interfaces.DisplayElement;

//当前天气状况
public class CurrentConditionsDisplay implements DisplayElement {

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========当前天气状况=========");
        System.out.println("温度:" + temperature);
        System.out.println("湿度:" + humidity);
        System.out.println("压强:" + pressure);
        System.out.println("========当前天气状况=========");
    }

}

ForecastDisplay

package headfirst.hd.observer.old.impl;

import java.util.Random;

import headfirst.hd.observer.old.interfaces.DisplayElement;

//天气预报
public class ForecastDisplay implements DisplayElement{

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========天气预报=========");

        //天气预报计算是一个相当复杂的过程,这里简单处理

        int randomNum = new Random().nextInt(10);
        randomNum %= 3;
        if (randomNum == 0) {
            System.out.println("天气晴朗");
        } else if (randomNum == 1) {
            System.out.println("暴雨");
        } else if (randomNum == 2) {
            System.out.println("多雨转晴");
        }

        System.out.println("========天气预报=========");
    }

}

StatisticsDisplay

package headfirst.hd.observer.old.impl;

import headfirst.hd.observer.old.interfaces.DisplayElement;

//天气统计
public class StatisticsDisplay implements DisplayElement {

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========当前天气状况=========");
        //天气统计是一个相当复杂的过程,这里简单处理
        System.out.println("未来几天,多云,适合外出");
        System.out.println("========当前天气状况=========");

    }

}

WeatherData

package headfirst.hd.observer.old;

import headfirst.hd.observer.old.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.old.impl.ForecastDisplay;
import headfirst.hd.observer.old.impl.StatisticsDisplay;
import headfirst.hd.observer.old.interfaces.DisplayElement;

public class WeatherData {
    private float temperature;
    private float humidity;
    private float pressure;

    public float getTemperature() {
        return temperature;
    }
    public float getHumidity() {
        return humidity;
    }
    public float getPressure() {
        return pressure;
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        measuremetnsChanged();
    }

    //方法任务单一原则
    public void measuremetnsChanged() {

        DisplayElement element1 = new CurrentConditionsDisplay();
        DisplayElement element2 = new ForecastDisplay();
        DisplayElement element3 = new StatisticsDisplay();

        element1.display(temperature, humidity, pressure);
        element2.display(temperature, humidity, pressure);
        element3.display(temperature, humidity, pressure);
    }
}

测试
DriveTest

package headfirst.hd.observer.old.test;

import headfirst.hd.observer.old.WeatherData;

public class DriveTest {

    public static void main(String[] args) {

        WeatherData weatherData = new WeatherData();

        //这里通过设置weatherData数据,代替硬件收集数据
        weatherData.setMeasurements(20, 30.2f, 59);
        weatherData.setMeasurements(26, 20.4f, 69);
        weatherData.setMeasurements(10, 36f, 94);

    }

}

运行结果

========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========
========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========
========当前天气状况=========
温度:10.0
湿度:36.0
压强:94.0
========当前天气状况=========
========天气预报=========
暴雨
========天气预报=========
========当前天气状况=========
未来几天,多云,适合外出
========当前天气状况=========

当前设计满足预期需求

当前例子引发的问题

当前设计为,WeatherData为管理数据与面板管理对象,且管理多个显示面板对象,一对多关系。容易引发的问题是,当面板(多)数据增加或者减少时,我们必须去修改WeatherData(一)代码,我们在设计代码时候希望对象之间是松耦合设计,及当某一个对象修改时候,不影响和其有关系的代码修改。
及简单抽象为以下三个问题

  • 当我们需要添加面板时候,不修改WeatherData中代码
  • 当我们需要坚守面板时候,不修改WeatherData中代码
  • 当我们修改WeatherData代码时候,不影响任何面板代码

观察者(Observer)模式_第4张图片

以后添加面板,怎么才不影响现有代码呢?

引入解决思想

当我们管理组数时候,有一批数据需要添加或者删除时候,我们有下面的思路
观察者(Observer)模式_第5张图片

  1. 默认添加入数据1,2,3
  2. 添加数据4
  3. 删除数据2
  4. 删除数据4

整个流程都不会出现以上三个问题

  • 当我们需要添加面板时候,不修改WeatherData中代码
  • 当我们需要坚守面板时候,不修改WeatherData中代码
  • 当我们修改WeatherData代码时候,不影响任何面板代码

解决办法,采用一定的数据结构解决该问题

重新设计

观察者(Observer)模式_第6张图片

对应代码

DisplayElement

package headfirst.hd.observer.news.interfaces;

public interface DisplayElement {
    void display(float temperature, float humidity, float pressure);
}

CurrentConditionsDisplay

package headfirst.hd.observer.news.impl;

import headfirst.hd.observer.news.interfaces.DisplayElement;

public class CurrentConditionsDisplay implements DisplayElement {

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========当前天气状况=========");
        System.out.println("温度:" + temperature);
        System.out.println("湿度:" + humidity);
        System.out.println("压强:" + pressure);
        System.out.println("========当前天气状况=========");
    }

}

ForecastDisplay

package headfirst.hd.observer.news.impl;

import java.util.Random;

import headfirst.hd.observer.news.interfaces.DisplayElement;

//天气预报
public class ForecastDisplay implements DisplayElement{

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========天气预报=========");

        //天气预报计算是一个相当复杂的过程,这里简单处理

        int randomNum = new Random().nextInt(10);
        randomNum %= 3;
        if (randomNum == 0) {
            System.out.println("天气晴朗");
        } else if (randomNum == 1) {
            System.out.println("暴雨");
        } else if (randomNum == 2) {
            System.out.println("多雨转晴");
        }

        System.out.println("========天气预报=========");
    }

}

StatisticsDisplay

package headfirst.hd.observer.news.impl;

import headfirst.hd.observer.news.interfaces.DisplayElement;

//天气统计
public class StatisticsDisplay implements DisplayElement {

    @Override
    public void display(float temperature, float humidity, float pressure) {
        System.out.println("========当前天气状况=========");
        //天气统计是一个相当复杂的过程,这里简单处理
        System.out.println("未来几天,多云,适合外出");
        System.out.println("========当前天气状况=========");

    }

}

WeatherData

package headfirst.hd.observer.news;

import java.util.ArrayList;

import headfirst.hd.observer.news.interfaces.DisplayElement;

public class WeatherData {
    private float temperature;
    private float humidity;
    private float pressure;
    private ArrayList observers = new ArrayList();

    public float getTemperature() {
        return temperature;
    }
    public float getHumidity() {
        return humidity;
    }
    public float getPressure() {
        return pressure;
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        measuremetnsChanged();
    }

    //方法任务单一原则
    public void measuremetnsChanged() {
        notifyObservers();
    }

    public void registerObserver(DisplayElement observer) {
        observers.add(observer);
    }

    public void removeObserver(DisplayElement observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (DisplayElement observer : observers) {
            observer.display(temperature, humidity, pressure);
        }
    }

}

测试
DriveTest

package headfirst.hd.observer.news.test;

import headfirst.hd.observer.news.WeatherData;
import headfirst.hd.observer.news.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.news.impl.ForecastDisplay;

public class DriveTest {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        //当前天气状态
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay();
        weatherData.registerObserver(conditionsDisplay);

        //天气预报
        ForecastDisplay forecastDisplay = new ForecastDisplay();
        weatherData.registerObserver(forecastDisplay);

        //这里通过设置weatherData数据,代替硬件收集数据
        weatherData.setMeasurements(20, 30.2f, 59);
        weatherData.setMeasurements(26, 20.4f, 69);
        weatherData.setMeasurements(10, 36f, 94);
    }

}

运行结果

========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
温度:10.0
湿度:36.0
压强:94.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========

解决以上三个问题

  • 当我们需要添加面板时候,不修改WeatherData中代码
  • 当我们需要坚守面板时候,不修改WeatherData中代码
  • 当我们修改WeatherData代码时候,不影响任何面板代码

观察者模式

为交互对象之间的松耦合设计,这里例子是一对多关系,涉及交互对象
例子一
这里写图片描述

观察者(Observer)模式_第7张图片

例子二
微博关注,如果你关注某人,你就会收到这个人的动态消息

  • 主动推动消息是可观察者
  • 接受消息的是观察者
  • 观察者有权选择关注和取消
  • 可观察者当消息变化时,会向观察者推送消息

例子三
当我们下载某款Android app时,我们成为了这款app的观察者,当app升级或者有什么动态时候,app会向我们推向消息

使用java内置观察者模式改造

对应设计图
观察者(Observer)模式_第8张图片

红色为系统内置文件

WeatherData

package headfirst.hd.observer.java.impl;

import java.util.Observable;

public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public float getTemperature() {
        return temperature;
    }
    public float getHumidity() {
        return humidity;
    }
    public float getPressure() {
        return pressure;
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        measuremetnsChanged();
    }

    //方法任务单一原则
    public void measuremetnsChanged() {
        //java内置方法有一个状态字段
        this.setChanged();
        notifyObservers();
    }
}

对应代码

CurrentConditionsDisplay

package headfirst.hd.observer.java.impl;

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

public class CurrentConditionsDisplay implements Observer {

    @Override
    public void update(Observable paramObservable, Object paramObject) {
        if (paramObservable instanceof WeatherData) {
            //拿到可观察者对象
            WeatherData weatherData = (WeatherData) paramObservable; 
            float temperature = weatherData.getTemperature();
            float humidity = weatherData.getHumidity();
            float pressure = weatherData.getPressure();

            System.out.println("========当前天气状况=========");
            System.out.println("温度:" + temperature);
            System.out.println("湿度:" + humidity);
            System.out.println("压强:" + pressure);
            System.out.println("========当前天气状况=========");
        }


    }

}

ForecastDisplay

package headfirst.hd.observer.java.impl;

import java.util.Observable;
import java.util.Observer;
import java.util.Random;

//天气预报
public class ForecastDisplay implements Observer {

    @Override
    public void update(Observable paramObservable, Object paramObject) {
        if (paramObservable instanceof WeatherData) {
            //拿到可观察者对象
            WeatherData weatherData = (WeatherData) paramObservable; 
            float temperature = weatherData.getTemperature();
            float humidity = weatherData.getHumidity();
            float pressure = weatherData.getPressure();

            System.out.println("========天气预报=========");

            //天气预报计算是一个相当复杂的过程,这里简单处理

            int randomNum = new Random().nextInt(10);
            randomNum %= 3;
            if (randomNum == 0) {
                System.out.println("天气晴朗");
            } else if (randomNum == 1) {
                System.out.println("暴雨");
            } else if (randomNum == 2) {
                System.out.println("多雨转晴");
            }

            System.out.println("========天气预报=========");
        }


    }

}

StatisticsDisplay

package headfirst.hd.observer.java.impl;

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

import headfirst.hd.observer.news.interfaces.DisplayElement;

//天气统计
public class StatisticsDisplay implements Observer {

    @Override
    public void update(Observable paramObservable, Object paramObject) {
        if (paramObservable instanceof WeatherData) {
            //拿到可观察者对象
            WeatherData weatherData = (WeatherData) paramObservable; 
            float temperature = weatherData.getTemperature();
            float humidity = weatherData.getHumidity();
            float pressure = weatherData.getPressure();

            System.out.println("========当前天气状况=========");
            //天气统计是一个相当复杂的过程,这里简单处理
            System.out.println("未来几天,多云,适合外出");
            System.out.println("========当前天气状况=========");
        }


    }

}

测试类

DriveTest

package headfirst.hd.observer.java.test;

import headfirst.hd.observer.java.impl.CurrentConditionsDisplay;
import headfirst.hd.observer.java.impl.ForecastDisplay;
import headfirst.hd.observer.java.impl.WeatherData;

public class DriveTest {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        //当前天气状态
        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay();
        weatherData.addObserver(conditionsDisplay);

        //天气预报
        ForecastDisplay forecastDisplay = new ForecastDisplay();
        weatherData.addObserver(forecastDisplay);

        //这里通过设置weatherData数据,代替硬件收集数据
        weatherData.setMeasurements(20, 30.2f, 59);
        weatherData.setMeasurements(26, 20.4f, 69);
        //weatherData.setMeasurements(10, 36f, 94);
    }

}

运行结果

========天气预报=========
天气晴朗
========天气预报=========
========当前天气状况=========
温度:20.0
湿度:30.2
压强:59.0
========当前天气状况=========
========天气预报=========
多雨转晴
========天气预报=========
========当前天气状况=========
温度:26.0
湿度:20.4
压强:69.0
========当前天气状况=========

满足预期需求设计,符合

java内置观察者模式源码

Observer(观察者实现的接口)

package java.util;

public abstract interface Observer
{
  public abstract void update(Observable paramObservable, Object paramObject);
}

Observable(可观察者继承的类)

package java.util;

public class Observable
{
  private boolean changed = false;
  private Vector obs = new Vector();

  public synchronized void addObserver(Observer paramObserver)
  {
    if (paramObserver == null) {
      throw new NullPointerException();
    }
    if (!this.obs.contains(paramObserver)) {
      this.obs.addElement(paramObserver);
    }
  }

  public synchronized void deleteObserver(Observer paramObserver)
  {
    this.obs.removeElement(paramObserver);
  }

  public void notifyObservers()
  {
    notifyObservers(null);
  }

  public void notifyObservers(Object paramObject)
  {
    Object[] arrayOfObject;
    synchronized (this)
    {
      if (!this.changed) {
        return;
      }
      arrayOfObject = this.obs.toArray();
      clearChanged();
    }
    for (int i = arrayOfObject.length - 1; i >= 0; i--) {
      ((Observer)arrayOfObject[i]).update(this, paramObject);
    }
  }

  public synchronized void deleteObservers()
  {
    this.obs.removeAllElements();
  }

  protected synchronized void setChanged()
  {
    this.changed = true;
  }

  protected synchronized void clearChanged()
  {
    this.changed = false;
  }

  public synchronized boolean hasChanged()
  {
    return this.changed;
  }

  public synchronized int countObservers()
  {
    return this.obs.size();
  }
}

理解字段changed

当changed字段为false时候,以后方法不会通知可观察者

public void notifyObservers(Object paramObject)
  {
    Object[] arrayOfObject;
    synchronized (this)
    {
      if (!this.changed) {
        return;
      }
      arrayOfObject = this.obs.toArray();
      clearChanged();
    }
    for (int i = arrayOfObject.length - 1; i >= 0; i--) {
      ((Observer)arrayOfObject[i]).update(this, paramObject);
    }
  }

所有在WeatherData类中,方法为以下实现

//方法任务单一原则
    public void measuremetnsChanged() {
        //java内置方法有一个状态字段
        this.setChanged();
        notifyObservers();
    }

为什么要这样考虑,以下解释
观察者(Observer)模式_第9张图片

温度变化1度,通知

public void setMeasurements(float temperature, float humidity, float pressure) {
        //简单处理
        if (temperature - this.temperature > 1) {
            this.setChanged();
        }

        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        measuremetnsChanged();
    }

    //方法任务单一原则
    public void measuremetnsChanged() {
        notifyObservers();
    }

你可能感兴趣的:(设计模式)