设计模式——观察者模式

观察者模式

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

2.构成
主题(subject)+订阅者(observer)=观察者模式
主题对象:管理某些数据; 当主题内的数据改变,就会通知观察者
观察者:已经订阅(注册)主题以便在主题数据改变时能够收到更新
观察者模式的类图
Subject:主题接口,对象使用此接口注册为观察者,或把自己从观察者中删除
ConcreteSubject:一个具体主题总是实现主题接口,除了注册和撤销方法之外,具体方法还实现了notifyObservers()方法,此方法用于在状态改变时更新所有当前观察者;具体主题也可能有设置和获取状态的方法
Observer:所有潜在的观察者都必须实现观察者接口,这个接口只有update()一个方法,当主题状态改变时它被调用
ConcreteObserver:具体的观察者可以是实现此接口的任意类。观察者必须注册具体主题,以便接受更新

3. 优点
观察者模式提供了一种对象设计,让主题和观察者之间

松耦合

4. 示例
气象监测应用
组成:

  • 气象站
  • WeatherData对象(追踪来自气象站的数据、更新布告板)
  • 布告板
    气象站类图
    CurrentConditionDisplay:此布告板根据WeatherData对象显示当前观测值
    StatisticsDisplay:此布告板根据最小、平均、最大的观测值,并显示它们
    ForecastDisplay:此布告板根据气压计显示天气预报
public interface Subject {
    // 注册观察者
    void registerObserver(Observer o);
    // 删除观察者
    void removeObserver(Observer o);
    // 主题状态改变时,通知所有的观察者
    void notifyObservers();
}

public class WeatherData implements Subject {
    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList<>();
    }
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i >= 0) {
            observers.remove(i);
        }
    }
    public void notifyObservers() {
        for(Observer o : observers) {
            o.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 interface Observer {
    // 当气象观测值改变时,把状态传递给观察者
    void update(float temp, float humidity, float pressure);
}

public interface DisplayElement {
    // 布告板显示信息
    void display();
}

public class CurrentConditionDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    public void display() {
        System.out.println("===============================");
        System.out.println("CurrentConditionDisplay: ");
        System.out.println("temperature: "+temperature);
        System.out.println("humidity: "+humidity);
        System.out.println("===============================");
    }
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        display();
    }
}

5. Java内置的观察者模式
java.util包内包含基本的Observer和Observable类
主题扩展自Observable类,并继承到一些增加、删除、通知观察者的方法(以及其他方法)

送出通知:

  1. 先调用setChanged()方法,标记状态已经改变的事实
  2. 然后调用两种notifyObservers()方法中的一个:
    notifyObservers()(拉式)
    notifyObservers(Object arg) 当通知时,此版本可以传送任何的数据对象给每一个观察者(推式)

观察者接受通知
update(Observable o, Object arg)
o:主题本身
arg:传入notifyObservers()的数据对象;如果没有说明则为空

1) 使用推(push)的方式传递数据
在一次通知中一口气通知观察者所有数据
notifyObservers(Object arg)
2) 使用拉(pull)的方式传递数据
主题提供公开的getter方法让观察者“拉”走数据
notifyObservers()

代码示例:
Java内置观察者类实现气象站的示例类图

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

    public WeatherData() {}
    public void measurementChanged() {
        setChanged();
        notifyObservers();
    }
    public void setMeasurements(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;

    public CurrentConditionDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
    @Override
    public void update(Observable o, Object arg) {
        if(arg instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)arg;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }
    @Override
    public void display() {
        System.out.println("===============================");
        System.out.println("CurrentConditionDisplay: ");
        System.out.println("temperature: "+temperature);
        System.out.println("humidity: "+humidity);
        System.out.println("===============================");
    }
}

不要依赖于观察者被通知的次序
java.util.Observable实现了它的notifyObservers()方法,这导致了通知观察者的次序并不同于我们先前的次序

Java内置Observable的缺点

  1. Observable是一个类,而不是一个接口
    Java不支持多继承,限制了Observable的复用潜力
  2. Observable将关键的方法保护起来
    setChanged()方法被保护起来(protected),除非继承Observable,否则无法创建Observable实例并组合到自己的对象中
    违反了设计原则:“多用组合,少用继承”

6. JDK中其他的观察者模式
Swing中的JButton
JavaBeans
RMI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值