观察者模式-订阅通知(一):Head first

 

需求分析

气象站提供一个WeatgerData类,里面有三个get方法,分别可以取到三个值:温度,湿度,气压。

 

我们需要实现三个布告板,分别是 “目前状态布告板”,“气象统计布告板”,“天气预告布告板。”

 

要求:

1、三个布告板都会用到气象站提供的数据。

2、并且一旦气象站数据发生变化,布告板上的数据也必须跟着变化。

3、布告板可以随心所欲的删除,或者添加新的布告板。

 

 

第一次设计:最“简单”的设计

public class WeatherData {

    //获取温度接口
    public float getTemperature(){return 0;}
    //获取湿度接口
    public float getHumidity(){return 0;}
    //获取气压接口
    public float getPressure(){return 0;}



    //一旦测量数据变化就调用,并且更新三个布告板
    public void measurementsChanged(){

        float temperature = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();

        //调用更新三个布告板

    }


}

 

很简单。但是如再新增一个布告板呢?WeatherData就要被修改一次。

现在目的要让布告板与气象站松耦合,并且想象一下假如有上百个布告板,我其中有一个不需要气象站的数据怎么办?

目前气象站是在强行喂饭给布告板啊。

 

分析

假如weatherData(气象站)是一个出版社,而布告板是订阅该出版社的人。

当出版社每次更新数据时,订阅者就可以收到出版社数据。 并且订阅者有权决定是否允许你给我传递数据。

 

问题在于,如何让气象站知道哪个布告板订阅了自己。

 

分析、抽象变动接口

为了解决以上问题,我们在气象站类中采用一个集合来记录订阅者。那么订阅者通过注册的途径来放入集合中。

根据策略模式,抽象出一个主题接口,专门用来注册订阅者,删除订阅者,修改订阅者(传递改变后的数据给订阅者)

public interface Subject {

    //注册观察者
    public void registerObserver(Observer observer);

    //删除观察者
    public void removeObserver(Observer observer);

    //主题状态改变时,通知所有观察者
    public void notifyObservers();
    
}

抽象出一个数据类,专门用来存放气象站的数据

public class NewData {

    float temperature;
    float humidity;
    float pressure;
    
}

 

 

抽象出一个订阅者超类,称为观察者。 当气象站的数据改变时,实现该接口的订阅者便会调用update()

public interface Observer {
    
    public void update(NewData newData);
    
}

 

抽象出一个布告板展示接口

public interface DisplayElement {

    public void display();
}

 

第二次设计

气象站实现了主题接口。并且实现了三个方法,注册观察者(订阅者)、删除、以及更新数据。

不要迷惑的地方:

setNewData(NewData newData); 这个方法是气象站数据改变的时候调用的,什么时候改变是气象站决定的,我们不设计,我们只给气象站提供一个外部访问接口。就是它们改变的时候需调用到我们这个接口方法。

public class WeatherData implements Subject {

    private List<Observer> observers;
    private NewData newData;

    public WeatherData(){
        //初始化记录集合
        observers = new ArrayList();
    }


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

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

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

    public void measurementsChanged(){
        notifyObservers();
    }

    public void setNewData(NewData newData){
        this.newData = newData;
        measurementsChanged();
    }
}

 

布告板实现

布告板实现了观察者接口,展示布告板接口。

刚才说过了,布告板就是订阅气象站数据的订阅者,观察者是订阅者抽象出来的接口。订阅者需要去注册,来表明自己需要气象站的数据。

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private NewData newData;
    private Subject weatherData;

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

    @Override
    public void display() {
        System.out.println("目前布告板数据是:"+newData);
    }

    @Override
    public void update(NewData newData) {
        this.newData = newData;
    }
}

 

测试

public class Test {

    public static void main(String[] args) {
     //声明一个气象站
        WeatherData weatherData = new WeatherData();
     //声明一个布告板,并且传入气象站,进行注册
        CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
     //新的数据
        NewData newData = new NewData();
     //一定newData对象变化,就设置新数据。 weatherData.setNewData(newData); } }

 

小节

会发现假如新增一个布告板,我只需要再new一个就好了嘛。 不需要再去进入weatherData类中进行修改了。

目前观察者(订阅者)还是被出版者(气象站) 强行喂饭。  凡是在气象站中注册了的布告板 都会被强行刷新数据。

 

 

 

 

 

定义观察者模式

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

可以理解为:一个出版社对应多个订阅者。

 

观察者模式-订阅通知(一):Head first

上一篇:单链表


下一篇:关于Dubbo提供者配置类中设置Service的版本,消费者@Reference找不到对应的版本