美文网首页
设计模式之-观察者模式Observer

设计模式之-观察者模式Observer

作者: professorrj | 来源:发表于2020-06-28 18:22 被阅读0次

生活中的观察者模式

想象一个报纸订阅情景:

  • 报社的业务就是出版报纸,向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来。只要你是他们的用户,你就会一直收到新的报纸
  • 当你不再需要看报的时候,取消订阅,他们就不会再送新报纸了
  • 同时,只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸

观察者模式UML(类图)

观察者模式

每个主题可以有多个观察者,对象使用主题中的registerObserver()注册为观察者,当主题状态改变时,notifyObservers()去更新所有当前观察者,观察者接口中的update()被调用。这样两个对象之间松耦合,依然可以交互,但不太清楚彼此的细节:

  • 主题只知道观察者实现了某个接口,不需要知道观察者的具体类是谁
  • 任何时候都可以增加删除观察者,主题不会受到任何影响,不需要为了支持新的类型而修改主题代码,只需在新的类里实现观察者接口,并注册为观察者即可
  • 可独立复用主题或者观察者

观察者模式实战

我们假设现在有个需求,是建立一个气象观测站。我们已经能从某天气数据中心拿到一个weatherData对象,它可以负责追踪目前的天气状况,我们需要建立三个相应的dashboard:当前数据,统计数据,和预测数据,来显示不同的气象数据。当weatherData获得最新的测量数据时,dashboard实时更新。

ok,那我们来尝试画一下这个应用的UML图:


talk is cheap, show me the code:

定义接口

public interface Subject {
    void regitsterObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObserver();
}

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

interface DisplayElement {
    void display();
}

weatherData实体类

public class WeatherData implements Subject {

    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList<>();
    }

    @Override
    public void regitsterObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(o);
        }
    }

    @Override
    public void notifyObservers() {
        for (int i = 0; i < observers.size(); i++) {
            observers.get(i).update(temperature, humidity, pressure);
        }
    }

    public void measurementChanged() {
        notifyObservers();
    }

    public void setMeasurement(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementChanged();
    }
}

建立三个dashboard,为了举例子,我们先只建currentCondition dashboard

public class CurrentConditionDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherData;

    public CurrentConditionDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.regitsterObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }

    @Override
    public void display() {
        System.out.println("Current weather condition: " + temperature
                + "F degrees, and " + humidity + "% humidity, and " + pressure + "pa");
    }
}

Cool! 现在我们可以写测试程序来看一下结果啦!

public class WeatherStation {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionDisplay conditionDisplay = new CurrentConditionDisplay(weatherData);
        weatherData.setMeasurement(80, 65, 30.5f);
    }
}
//输出: Current weather condition: 80.0F degrees, and 65.0% humidity, and 30.5pa

以上,当我们需要加入新的或者删除observer时,只需要加入/删除相应实现了Observer接口的类就可以了,并不需要对Subject做任何改动。

Java内置的观察者模式

以上的例子中,主题每次更新数据时候,需要push到每个订阅了它的观察者,这样主题就是有状态的了,它需要知道观察者怎么去修改数据,是否可以提供一种pull的形式,主题只需要提供一些getter方法,这样由观察者去自己做出对所需数据的修改,而当主题增加新的状态类型的时候,它只需要相应增加getter方法就可以了。

在Java中,java.util.Observable就类似与我们上面的Subject,而java.util.Obsever就是观察者。Observable内置了addObsever(), deleteObsever(), notifyObserver(), setChanged()等方法,conreteClass可以去继承,而Observer接口里只有一个update(Observable o, Object arg)方法,如果你想push,你可以调用Observable里的notifyObservers(args)方法,否则,观察者必须从可观察对象中pull,让我们调整上面代码的例子,来使用Java内置的观察者模式:

public class WeatherData extends Observable {

    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
    }

    public void measurementChanged() {
        setChanged();//只有把changed flag设为true,notifyObservers()才会通知
        notifyObservers();
    }

    public void setMeasurement(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}


public class CurrentConditionDisplay implements Observer, DisplayElement {

    Observable observable;
    private float temperature;
    private float humidity;
    private float pressure;

    public CurrentConditionDisplay(Observable observable) {
        this.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();
            this.pressure = weatherData.getPressure();
            display();
        }
    }

    @Override
    public void display() {
        System.out.println("Current weather condition: " + temperature
                + "F degrees, and " + humidity + "% humidity, and " + pressure + "pa");
    }

}

一些思考

我们可以发现,java.util.Observable是一个类,而不是接口,你必须设计一个类去继承它,如果这个类想同时拥有另一个超类的功能,那么就会陷入两难,因为Java不支持多继承 ,而你又必须继承Observable来创建实例并才能调用setChanged()方法,这又违反了“favor composition over inheritance”的原则。所以,如果你能够扩展Observable,那么它可能会符合你的要求,否则,你可能需要像开头一样,自己实现一套观察者模式了。

之后,我们会看到观察者模式的代表人物:MVC

相关文章

网友评论

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

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