Head First设计模式学习笔记二

观察者模式

Posted by Emo on June 23, 2022

设计模式

练习实例仓库

EMO’s Blog

定义

观察者模式定义

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

OO原则

  1. 封装变化
  2. 多用组合,少用继承
  3. 针对接口编程,不针对实现编程
  4. 为交互对象之间的松耦设计而努力

气候站实例

描述

观察站通过硬件获取气候条件数据(Temp, Humidity and Pressure),然后自己设计各种看板实时展示这些数据。要求当观察站检测到的气候数据变化后,将最新的数据通知给各个看板,而后看板自动更新展示详情。

设计

在这个实例中我们使用观察者模式来设计,首先,我们可以分辨出气候站对象是主题(subject/observable),而各种看板则是观察者(observer)。Observable需要知道所有跟随他的Observer的信息,并以此通知他们,所以Observer需要能够向Obervable注册自己,并且也需要能够随时脱离。 而后我们设计两种角色需要设计到的功能: Observable

  1. 添加observer
  2. 删除observer
  3. 通知observers

Observer

  1. 更新自身状态

根据OO原则我们开始设计接口类:

public interface Observable {
		public void addObserver(Observer observer);
		public void deleteObserver(Observer observer);
		public void notifyObservers();
}

public interface Observer {
		public void update();
}

接口类设计好后,我们可以开始针对接口进行具体的实现了。在观察者模式里我们需要构建一对多关系,也就是说Observable和Observers之间的关系是一对多关系。因此我们这里提出了一种新的OO原则

为交互对象之间的松耦设计而努力

因此在实现具体的Observable的时候,我们间关系的构建映射到管理Observers接口类,就可以将具体的Observable实现与Observers之间解耦。我们在需要修改或者新增各种不同的Observers的时候,就不需要改动Observable的相关代码了。

具体对象的实现

WeatherData(Observable具体实现)

package WeatherApplication.Subject;

import WeatherApplication.Observer.Observer;

import java.util.ArrayList;

public class WeatherData implements Subject {
    private ArrayList observers;
    private float temp;
    private float humidity;
    private float pressure;

    public WeatherData() {
        this.observers = new ArrayList();
    }

    @Override
    public void registerObserver(Observer observer) {
        this.observers.add(observer);
    }

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

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

    public void measurementChanged() {
        this.notifyObservers();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        this.measurementChanged();
    }
}

CurrentConditionDisplay(看板的一种实现)

package WeatherApplication.Observer;

import WeatherApplication.Subject.Subject;

public class CurrentConditionsDisplay implements Observer, DisplayElement{
    private float temp;
    private float humidity;
    private Subject mySubject;

    public CurrentConditionsDisplay(Subject subject) {
        this.mySubject = subject;
        this.mySubject.registerObserver(this);
    }

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

    @Override
    public void display() {
        System.out.println("Current Conditions: " + this.temp + " " + this.humidity);
    }
}

进一步设计

上述实现当中,我们可以看到,在定义update方法的时候,我们需要指定传输进去的变量,那么存在一个问题,为了保证接口的统一,我们会将Observers可能不需要的数据也一并传给他。那么Observers就像,能否他们需要什么数据自己去向Observable要呢?这样Observable只需要告知Observers数据更新这个事件的发生,而后Observers自己通过Observable提供的getter方法去获取自己想要的数据。Java.util包当中,提供了这种方法,在notifyObservers的方法调用的时候,Observable调用Observers的update方法不传输任何数据信息,而是将自己的整个对象的引用传过去,而后Observers根据对象引用调取getter方法以获取想要的数据。

@Override
public void update(Observable o, Object arg) {
    if (o instanceof WeatherData) {
        WeatherData weatherData = (WeatherData) o;
        this.temp = weatherData.getTemp();
        this.humidity = weatherData.getHumidity();
        this.display();
    }
}

上面代码中,我们就可以看到,Observers并没有通过arg来获取数据,而是调用对象o中的getter方法来获取到了指定的数据。

/*
    Java自带的Observer接口,同样定义了update方法,只不过传入的参数变了,
    update(Observable o, Object arg)
    数据以Object的形式传入,
    Observable则用于Observer来识别接收到信息来自于哪一个subject。
*/
package WeatherApplicationWithJava;

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

public class CurrentConditionDisplay implements Observer {
    private float temp;
    private float humidity;
    private Observable subject;

    public CurrentConditionDisplay(Observable subject) {
        this.subject = subject;
        this.subject.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            this.temp = weatherData.getTemp();
            this.humidity = weatherData.getHumidity();
            this.display();
        }
    }

    public void display() {
        System.out.println("Current Conditions: " + this.temp + " " + this.humidity);
    }
}


{ % if page.mermaid % } { % endif % }