设计模式 (二) —— 观察者模式

一、含义

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

二、设计原则

1.封装变化。

2.多用组合,少用继承。

3.针对接口编程,不针对实现编程。

4.为交互对象之间的松耦合设计而努力。

三、要点

1.观察者模式定义了对象之间一对多的关系。

2.主题(也就是可观察者)用一个共同的接口来更新观察者。

3.观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。

4.使用此模式时,你可从被观察者处(push)或拉(pull)数据(然而,推的方式被认为更“正确”)。

5.有多个观察者时,不可以依赖特定的通知次序。

三、实战分析策略模式

需求:根据气象站获得的数据(包括温度,湿度,其它),实时显示在不同的布告板上(现在是三套布告板)。

扫描二维码关注公众号,回复: 4119911 查看本文章

我们首先设计一个WeatherData类,来获得气象测试数据。

需求分析:我们需要三个使用天气数据的布告板,一但WeatherData有新的测量,这些布告必须马上更新,并且此系统应该需要可扩展,可以让其它开发人员可以自定义布告板,并且用户可以随心所欲的添加和删除布告板。 

其实观察模式就是:出版者,可称为主题(subject)+订阅者,可称为观察者(observer)。

从图中可以看到,主题和观察者定义了一对多的关系。观察者依赖此主题,只要主题状态一有改变,观察者就会被通知。根据通知的风格,观察者可能因此新值而进行更新。

具体类图设计如下

松耦合的威力就是当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

四、代码演示

1.先定义接口

/**
 * 主题实现的接口
 */
public interface Subject {

    /**
     * 添加观察者
     * @param o
     */
    public void registerObserver(Observer o);

    /**
     * 删除观察者
     * @param o
     */
    public void removeObserver(Observer o);


    /**
     * 当主题状态改变时,这个方法调用,通知所有观察者
     */
    public void notifyObservers();

}


/**
 * 所有观察者必须实现的接口
 */
public interface Observer {

    /**
     * 跟新显示数据
     * @param temp
     * @param humidity
     * @param pressure
     */
    public void update(float temp, float humidity, float pressure);
}


public interface DisplayElement {

    /**
     * 当布告板需要显示时,调用此方法
     */
    public void display();
}

2.主题具体的实现类

/**
 * WeatherData实现主题接口
 */
public class WeatherData implements Subject {

    /** 用来存放观察者 */
    private ArrayList observers;

    private float temperature;

    private float humidity;

    private float pressure;

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


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

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

    }

    @Override
    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();
    }

    /**
     * 数据变化时,调用此方法
     * @param temperature
     * @param humidity
     * @param pressure
     */
    public void setMeasurements(float temperature,float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();

    }
}

3.观察者具体的实现类

/**
 * 某一个布告板的具体实现
 */
public class CurrentConditionsDisplay implements Observer,DisplayElement {

    private float temperature;

    private float humidity;

    private Subject weatherData;

    /**
     * 构造器需要将weatherData对象(也就是主题)作为注册用。
     * @param weatherData
     */
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature
            + "F degrees and " + humidity + "% humidity");

    }

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

4.编写测试类

public class WeatherStationTest {

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

        weatherData.setMeasurements(70,65,22.5f);
        weatherData.setMeasurements(78,68,20.1f);
        weatherData.setMeasurements(88,79,21.1f);

    }
}

5.运行结果

Current conditions: 0.0F degrees and 65.0% humidity
Current conditions: 0.0F degrees and 68.0% humidity
Current conditions: 0.0F degrees and 79.0% humidity

五、扩展思维

在上面的观察者模式实现过程中,观察者只能等待主题去推送消息,而不能自己去拉数据。其实在内置的java观察者模式中,分为两种,一个是主题向观察者推送消息,一个是观察者去拉消息。

拉消息的类图如下所示:

主题在发送消息时,先调用setChanged()方法,标记状态已经改变的事实。然后调用两种notifyObservers()方法中的一个。

具体代码实现如下:

主题的实现

/**
 * WeatherData实现主题接口
 */
public class WeatherData  extends Observable {

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

    /**
     * 构造器中不在需要为了记住观察者们而建立数据结构了
     */
    public WeatherData() {
    }
    
    public void measurementsChanged() {
        setChanged();
        notifyObservers(); //没有调用这个方法去推消息,而是让观察者去拉消息
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
        
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

观察者的实现代码:

/**
 * 某一个布告板的具体实现
 */
public class CurrentConditionsDisplay implements Observer,DisplayElement {

    Observable observable;

    private float temperature;

    private float humidity;


    /**
     * 将对象注册为观察者
     * @param observable
     */
    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

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

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

运行结果如上。

文章内容参考《Head First 设计模式》

猜你喜欢

转载自blog.csdn.net/huxiaodong1994/article/details/83386224