设计模式笔记:观察者模式

概念

观察者模式,也叫做发布-订阅模式,定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖着都会收到通知并自动更新。

组成

注意

观察者模式有很多实现方式,从根本上说,该模式必须包含两个角色:主题和观察者。

实现观察者模式的时候要注意,主题和观察者对象之间的互动关系不能体现成类之间的直接调用,否则就将使主题和观察者对象之间紧密的耦合起来,从根本上违反面向对象的设计的原则。无论是观察者“观察”观察对象,还是主题将自己的改变“通知”观察者,都不应该直接调用。

应用举例

假如你的团队刚刚赢得了一纸合约,负责建立Weather-O-Rama公司的下一代气象站-Internet气象观测站。

气象监测应用的概况

我们的工作就是建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。

设计气象站

实现气象站

public interface Subject{
    // 这两个方法都需要一个观察者作为变量,该观察者是用来注册或者删除的。
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    
    // 当主题状态改变时,这个状态会被调用,以通知所有的观察者。
    public void notifyObservers();
}
public interface Observer{
    // 所有观察者都必须实现update()方法,以实现观察者接口。
    public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement{
    // 只包含一个方法display(),当布告板需要显示时,调用此方法。
    public void display();
}

在WeatherData中实现主题接口

public class WeatherData implements Subject{
    // Arraylist用来记录观察者,稍后在构造器中建立。
    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;
    
    private WeatherData(){
        observers = new ArrayList();
    }
    
    private void registerObserver(Observer o){
        observers.add(o);
    }
    
    private void removeObserver(Observer o){
        int i = observers.indexOf(o);
        if(i > 0){
            obervers.remove(i);
        }
    }
    
    // 当主题状态变更时,通知所有观察者
    public void notifyObservers(){
        for(int i = 0; i < observers.size(); i++){
            Observer observer = (Observer)observers.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }
    
    public void measurementsChanged(){
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

建立布告板

public class CurrentConditionsDisplay implements Observer, DisplayElement{
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherData;
    
    public CurrentConditionsDisplay(Subject weatherData){
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    
    public void update(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
    
    public void display(){
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}

启动气象站

public class WeatherStation{
    public static void main(String[] args){
        WeatherData weatherData = new WeatherData();
        
        // 建立布告板,并发weatherData对象传入
        CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);
        
        weatherData.setMeasurements(806530.4f);
        
        // 当主题状态更改时,会通知所有观察者更新数据
        weatherData.setMeasurements(705520.4f);
    }
}

运行程序

%java WeatherStation
Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 70.0F degrees and 55.0% humidity