设计模式(行为型设计模式——观察者模式)

时间:2024-03-22 16:08:02

设计模式(行为型设计模式——观察者模式)

观察者模式

基本定义

  • 指多个对象间存在一对多的依赖关系,这样一来,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

  • 这种模式又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

模式结构

  • Subject 抽象主题 :也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。

  • ConcreteSubject具体主题:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。

  • Observer 观察者:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。

  • ConcreteObserver 具体观察者:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

代码实现

Subject 抽象主题
/**
 * @Description 抽象主题类
 **/
public interface Subject {

    //注册观察者
    void registerObserve(Observe observe);

    //移除观察者
    void removeObserve(Observe observe);

    //通知观察者
    void notifyAllObserve();
}
ConcreteSubject 具体主题
/**
 * @Description 具体主题
 **/
@Slf4j
public class WeatherSubject implements Subject {

    List<Observe> observes = new ArrayList<>();

    Weather weather;

    /**
     * 实现特定顺序通知观察者,可以自我实现排序方式
     * 如果使用队列保存观者,需提前排序
     * @param observe
     */
    @Override
    public void registerObserve(Observe observe) {
        observes.add(observe);
    }

    @Override
    public void removeObserve(Observe observe) {
        log.info("移除观察者 {}", observe.name());
        observes.remove(observe);
    }


    /**
     * 方式一、直接将更新内容发送给观察者
     * 推模式:通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。
     *
     * 方式二、通知消息给观察者拉去更新数据
     * 拉模式:目标角色在发生变化后,仅仅告诉观察者角色“我变化了”;观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。
     *
     */
    @Override
    public void notifyAllObserve() {
        if(observes.isEmpty()){
            log.info("暂无观察者");
            return;
        }
        observes.stream().forEach((observe)->{
            observe.update(weather);
        });
    }

    /**
     * 天气更新,通知观察者。
     * 与监听者模式差别在于 事件处理(事件源 + 事件 + 事件监听)
     * @param weather
     */
    public void setWeather(Weather weather) {
        this.weather = weather;
        notifyAllObserve();

        //监听模式,事件处理。可以使用外观模式封装事件处理
        //eventHandle.notifyAllObserve(observes)
    }
}
POJO 普通Java实体:通知内容封装
@Data
public class Weather {

    private String date;
    private int temperature;
    private int humidity;
    private int windPower;

    public Weather(String date, int temperature, int humidity, int windPower) {
        this.date = date;
        this.temperature = temperature;
        this.humidity = humidity;
        this.windPower = windPower;
    }
}
Observer 观察者
/**
 * @Description 抽象观察者
 **/
public interface Observe {

    String name();

    /**
     * @param weather 天气
     */
    void update(Weather weather);
}
ConcreteObserver 具体观察者
/**
 * @Description 具体观察者(APP客户端)
 **/
@Slf4j
public class AppClientObserve implements Observe {

    private Subject weatherSubject;

    //注册观察者, 关联主题
    public AppClientObserve(Subject weatherSubject) {
        this.weatherSubject = weatherSubject;
        this.weatherSubject.registerObserve(this);
    }


    @Override
    public String name() {
        return "App客户端";
    }

    @Override
    public void update(Weather weather) {
        log.info("APP客户端:{} 最新天气:温度 {} ℃, 相对湿度 {} %, 风力 {} 级",
                weather.getDate(), weather.getTemperature(), weather.getHumidity(), weather.getWindPower());
    }
}
/**
 * @Description 具体观察者(H5客户端)
 **/
@Slf4j
public class H5ClientObserve implements Observe {

    private Subject weatherSubject;

    //注册观察者, 关联主题
    public H5ClientObserve(Subject weatherSubject) {
        this.weatherSubject = weatherSubject;
        this.weatherSubject.registerObserve(this);
    }


    @Override
    public String name() {
        return "H5客户端";
    }

    @Override
    public void update(Weather weather) {
        log.info("H5客户端:{} 最新天气:温度 {} ℃, 相对湿度 {} %, 风力 {} 级",
                weather.getDate(), weather.getTemperature(), weather.getHumidity(), weather.getWindPower());
    }
}
Client 客户端:测试类
/**
 * @Description 观察者测试类
 **/
public class Test {
    public static void main(String[] args) {
        WeatherSubject weatherSubject = new WeatherSubject();
        Observe h5Observe = new H5ClientObserve(weatherSubject);
        Observe appObserve = new AppClientObserve(weatherSubject);

        weatherSubject.setWeather(new Weather("2020-4-16 12:00",24, 50, 2));
        weatherSubject.setWeather(new Weather("2020-4-16 16:00",20, 70, 3));

        weatherSubject.removeObserve(h5Observe);
        weatherSubject.setWeather(new Weather("2020-4-16 18:00",15, 65, 1));
    }
}
输出结果

H5客户端:2020-4-16 12:00 最新天气:温度 24 ℃, 相对湿度 50 %, 风力 2 级

APP客户端:2020-4-16 12:00 最新天气:温度 24 ℃, 相对湿度 50 %, 风力 2 级

H5客户端:2020-4-16 16:00 最新天气:温度 20 ℃, 相对湿度 70 %, 风力 3 级

APP客户端:2020-4-16 16:00 最新天气:温度 20 ℃, 相对湿度 70 %, 风力 3 级

移除观察者 H5客户端

APP客户端:2020-4-16 18:00 最新天气:温度 15 ℃, 相对湿度 65 %, 风力 1 级

优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。

  • 目标与观察者之间建立了一套触发机制。

缺点

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。

  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

使用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

总结

  • 观察者模式定义了对象之间的一对多关系。多个观察者监听同一个被观察者,当该被观察者的状态发生改变时,会通知所有的观察者。

  • 观察者与被观察者之间用松耦合方式结合。

  • 使用观察者模式,可以从被观察者处推或者拉数据。