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

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

观察者模式

基本定义

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

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

模式结构

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

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

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

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

代码实现

Subject 抽象主题
java 复制代码
/**
 * @Description 抽象主题类
 **/
public interface Subject {

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

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

    //通知观察者
    void notifyAllObserve();
}
ConcreteSubject 具体主题
java 复制代码
/**
 * @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实体:通知内容封装
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 观察者
java 复制代码
/**
 * @Description 抽象观察者
 **/
public interface Observe {

    String name();

    /**
     * @param weather 天气
     */
    void update(Weather weather);
}
ConcreteObserver 具体观察者
java 复制代码
/**
 * @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());
    }
}
java 复制代码
/**
 * @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 客户端:测试类
java 复制代码
/**
 * @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 级

优点

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

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

缺点

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

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

使用场景

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

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

总结

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

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

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

相关推荐
牙牙7055 分钟前
Centos7安装Jenkins脚本一键部署
java·servlet·jenkins
paopaokaka_luck12 分钟前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
以后不吃煲仔饭25 分钟前
Java基础夯实——2.7 线程上下文切换
java·开发语言
进阶的架构师26 分钟前
2024年Java面试题及答案整理(1000+面试题附答案解析)
java·开发语言
The_Ticker31 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
小白不太白9501 小时前
设计模式之 观察者模式
观察者模式·设计模式
大数据编程之光1 小时前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
爪哇学长1 小时前
双指针算法详解:原理、应用场景及代码示例
java·数据结构·算法
ExiFengs1 小时前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj1234567891 小时前
JDK1.8新增特性
java·开发语言