观察者模式(Observer Pattern)

观察者模式,主要就是一个地方的改变,需要通知多个其他依赖方,例如,一个订单的状态发生变更,后续的物流、保险等许多系统都需要用到这个变化值,那么就可以考虑使用观察者模式(这里不考虑跨系统问题)

在JDK中,Swing API就用到了观察者模式,放一个按钮被触发时,所有需要依赖该通知的地方,都会收到通知事件,收到通知事件后,做相应的处理。

在观察者模式中,一般存在三个对象,一个是通知对象,一个是主题Subject,一个是具体的需要被通知的对象。

接下来就演示一个观察者模式

示例:一个天气监测站,每有数据更新,则通知各个显示站,显示站根据自己的需求进行展示内容

首先,创建一个主题接口,用来注册、移除观察者,同时可以通知观察者

package lcl.mm.pattern.observer.demo1;

public interface Subject {
    /**
     * 注册一个观察者
     * @param o
     */
    public void registerObserver(MyObserver o);

    /**
     * 移除一个观察者
     * @param o
     */
    public void removeOberver(MyObserver o);

    /**
     * 通知观察者
     */
    public void notifyObserver();
}

然后,创建一个展示内容的接口

package lcl.mm.pattern.observer.demo1;

public interface DisplayElement {
    public void display();
}

接着,创建一个更新接口

package lcl.mm.pattern.observer.demo1;

public interface MyObserver {
    public void update(int temp,int humidity, int pressure);
}

创建一个通知的对象,该对象实现Subject接口,对注册、移除和通知观察者接口进行实现,观察通知着的实现是循环所有观察者进行调用更新方法,然后还提供一个供天气监测站调用的接口

package lcl.mm.pattern.observer.demo1;

import java.util.ArrayList;

public class WeatherData implements Subject{

    private ArrayList<MyObserver> observers = new ArrayList<>();
    private int temperature;
    private int humidity;
    private int pressure;

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

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

    @Override
    public void notifyObserver() {
        for (MyObserver o:observers) {
            o.update(temperature,humidity,pressure);
        }
    }

    /**
     * 数据变更
     */
    public void setChangeed(int temperature,int humidity, int pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        this.notifyObserver();
    }
}

最后就是创建观察者对象,该对象实现数据更新接口和显示接口,在显示接口实现方法中,按照自己的需求显示内容;在更新接口实现方法中,按照自己的需求做数据更新并调用显示方法(这里写了两个观察者)

package lcl.mm.pattern.observer.demo1;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CurrentConditionDisplay implements MyObserver,DisplayElement {

    private int temperature;
    private int humidity;
    private int pressure;
    private WeatherData weatherData;

    @Override
    public void display() {
        log.info("CurrentConditionDisplay:{},{},{}",temperature,humidity,pressure);
    }

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

    public CurrentConditionDisplay(WeatherData weatherData){
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
}
package lcl.mm.pattern.observer.demo1;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TotalConditionDisplay implements MyObserver,DisplayElement {

    private int temperature;
    private int humidity;
    private int pressure;
    private WeatherData weatherData;

    @Override
    public void display() {
        log.info("TotalConditionDisplay:{},{},{}",temperature,humidity,pressure);
    }

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

    public TotalConditionDisplay(WeatherData weatherData){
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
}

最后,就是测试,写一个测试方法,通知上述两个观察者,然后再演示剔除一个观察者后再通知所有观察者

    @Test
    void observerTest1(){
        WeatherData weatherData = new WeatherData();
        CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay(weatherData);
        TotalConditionDisplay totalConditionDisplay = new TotalConditionDisplay(weatherData);
        log.info("register observer:CurrentConditionDisplay,TotalConditionDisplay");
        weatherData.setChangeed(1,2,3);
        weatherData.setChangeed(4,5,6);
        log.info("remove register currentConditionDisplay");
        weatherData.removeOberver(currentConditionDisplay);
        weatherData.setChangeed(5,6,7);
    }

输出结果

 

 其实,在jdk中,已经提供了观察者模式的相关类java.util.Observable和java.util.Observer,那么使用JDK提供的实现类来写上述观察者模式,写法如下:

显示的接口,与上述一致

package lcl.mm.pattern.observer.demo2;

public interface DisplayElement2 {
    public void display();
}

创建一个传输数据对象,该对象需要继承java.util.Observable,在该类的调用前notifyObservers()通知观察者之前,需要先调用setChanged();将变更状态置为true,否则不会通知观察者。这是为了给我们提供一个灵活的更新限制,比如说,小于5的情况下,我们就不更新。

package lcl.mm.pattern.observer.demo2;

import java.util.Observable;

public class WeatherData2 extends Observable {

    private int temperature;
    private int humidity;
    private int pressure;

    public void changed(int temperature, int humidity, int pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyChanged();
    }

    private void notifyChanged(){
        setChanged();
        notifyObservers();
    }

    public int getTemperature(){
        return temperature;
    }

    public int getHumidity(){
        return  humidity;
    }

    public int getPressure(){
        return pressure;
    }
}

然后,写两个观察者,需要实现java.util.Observer接口和自己创建的展示接口

package lcl.mm.pattern.observer.demo2;

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class CurrentConditionDisplay2 implements Observer, DisplayElement2 {

    private int temperature;
    private int humidity;
    private int pressure;
    private Observable observable;
    @Override
    public void display() {
        log.info("CurrentConditionDisplay2:{},{},{}",temperature,humidity,pressure);
    }

    public CurrentConditionDisplay2(Observable observable){
        this.observable = observable;
        observable.addObserver(this);
    }


    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData2){
            WeatherData2 weatherData = (WeatherData2) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            this.pressure = weatherData.getPressure();
            display();
        }
    }
}
package lcl.mm.pattern.observer.demo2;

import lombok.extern.slf4j.Slf4j;

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

@Slf4j
public class TotalConditionDisplay2 implements Observer, DisplayElement2 {

    private int temperature;
    private int humidity;
    private int pressure;
    private Observable observable;

    @Override
    public void display() {
        log.info("TotalConditionDisplay2:{},{},{}",temperature,humidity,pressure);
    }


    public TotalConditionDisplay2(Observable observable){
        this.observable = observable;
        observable.addObserver(this);
    }

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

最后,编写测试方法

    @Test
    void observerTest2(){
        WeatherData2 weatherData = new WeatherData2();
        log.info("register observer:CurrentConditionDisplay,TotalConditionDisplay");
        CurrentConditionDisplay2 currentConditionDisplay2 = new CurrentConditionDisplay2(weatherData);
        TotalConditionDisplay2 totalConditionDisplay2 = new TotalConditionDisplay2(weatherData);
        weatherData.changed(1,2,3);
        weatherData.changed(4,5,6);

        
        log.info("remove register currentConditionDisplay");
        weatherData.deleteObserver(currentConditionDisplay2);
        weatherData.changed(7,8,9);
    }

测试:

 

posted @ 2020-07-02 02:10  李聪龙  阅读(229)  评论(0编辑  收藏  举报