观察者模式(二十)

相信自己,请一定要相信自己

上一章简单介绍了迭代器模式(十九), 如果没有看过, 请观看上一章

一. 观察者模式

引用 菜鸟教程里面 观察者模式介绍: https://www.runoob.com/design-pattern/observer-pattern.html

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。

比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式

一.一 介绍

意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

主要解决: 一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用: 一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决: 使用面向对象技术,可以将这种依赖关系弱化。

关键代码: 在抽象类里有一个 ArrayList 存放观察者们。

应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。


组成角色具体关系
抽象观察者角色Observer为所有的具体观察者定义一个接口,在得到主题通知时更新自己
具体观察者角色Baidu, Sina, User实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调
抽象被观察者角色Subject一个抽象主题,它把所有对观察者对象的引用保存在一个集合中
具体被观察者角色WeatherDataSubject一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知

image-20230615184354714

二. 观察者实例

如观察天气的情况

二.一 观察的对象实体 Weather

@Data
@Builder
public class Weather {
    /**
      温度, 气压, 湿度
     */
    private float temperature;
    private float pressure;
    private float humidity;
}

二.二 抽象观察者 Observer

public interface Observer {

    public void update (Weather weather);
}

二.三 具体观察者实现

二.三.一 百度网站

@Slf4j
public class BaiDu implements Observer{
    private Weather weather ;
    @Override
    public void update(Weather weather) {
        this.weather = weather;
        display();
    }

    public void display (){
        if (weather == null) {
            return ;
        }
        log.info(">>>>>>>>>>>>>百度网站获取到天气数据,进行个性化操作");
        log.info(">>>温度: {}, 气压: {}, 湿度: {}", weather.getTemperature(),weather.getPressure(),
                weather.getHumidity());
    }

}

二.三.二 新浪网站

@Slf4j
public class Sina implements Observer{
    private Weather weather ;
    @Override
    public void update(Weather weather) {
        this.weather = weather;
        display();
    }

    public void display (){
        if (weather == null) {
            return ;
        }
        log.info(">>>>>>>>>>>>>新浪网站获取到天气数据,进行个性化操作");
        log.info(">>>温度: {}, 气压: {}, 湿度: {}", weather.getTemperature(),weather.getPressure(),
                weather.getHumidity());
    }

}

二.三.三 订阅者

@Slf4j
public class User implements Observer{
    private Weather weather ;
    @Override
    public void update(Weather weather) {
        this.weather = weather;
        display();
    }

    public void display (){
        if (weather == null) {
            return ;
        }
        log.info(">>>>>>>>>>>>>订阅用户获取到天气数据,进行个性化操作");
        log.info(">>>温度: {}, 气压: {}, 湿度: {}", weather.getTemperature(),weather.getPressure(),
                weather.getHumidity());
    }

}

二.四 抽象被观察者 Subject

public interface Subject {
    public void registerObserver(Observer observer);

    public void remove (Observer observer);

    public void notifyObservers();
}

二.五 具体观察者实现

@Slf4j
public class WeatherDataSubject implements Subject{
    private List<Observer> observerList = new ArrayList<>();
    private Weather weather;
    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }

    @Override
    public void remove(Observer observer) {
        observerList.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observerList) {
            observer.update(this.weather);
        }
    }

    public void setWeather(Weather weather) {
        this.weather = weather;
        dataChange();
    }

    public void dataChange() {
        // 更新后,数据发生了改变。
        log.info("获取到了最新的数据: {}", this.weather);
        notifyObservers();
    }
}

二.六 客户端调用

  @Test
    public void oneTest() {
        WeatherDataSubject weatherDataSubject = new WeatherDataSubject();
        weatherDataSubject.registerObserver(new BaiDu());
        weatherDataSubject.registerObserver(new Sina());

        // 设置数据
        weatherDataSubject.setWeather(
                Weather.builder()
                        .temperature(35.0f)
                        .pressure(20.0f)
                        .humidity(30.f)
                        .build()
        );
        User user = new User();
        // 再添加个数据
        weatherDataSubject.registerObserver(user);

        weatherDataSubject.setWeather(
                Weather.builder()
                        .temperature(30.0f)
                        .pressure(21.0f)
                        .humidity(32.f)
                        .build()
        );


        weatherDataSubject.remove(user);


        weatherDataSubject.setWeather(
                Weather.builder()
                        .temperature(32.0f)
                        .pressure(22.0f)
                        .humidity(33.f)
                        .build()
        );

    }

image-20230615185146367

三. Java 实现观察者模式

Java 语言, 可以通过 Observer(Observer) Observable(Subject) 实现观察者处理

三.一 具体观察者和抽象观察者

@Slf4j
public class NewUser implements Observer {
    private String name;
    public NewUser(String name) {
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        log.info("{} 观察到信息发生了变化 {}", name,arg);
    }
}

三.二 具体被观察者和抽象被观察者

public class NewWeatherDataSubject extends Observable {
    private Weather weather;

    public NewWeatherDataSubject (Weather weather) {
        this.weather = weather;
    }

    public void setWeather(Weather weather) {
        this.weather = weather;
        // 改变了
        super.setChanged();
        super.notifyObservers(weather);
    }
}

三.三 客户端调用

@Test
    public void twoTest() {
        Weather weather = Weather.builder()
                .temperature(32.0f)
                .pressure(22.0f)
                .humidity(33.f)
                .build();
        NewWeatherDataSubject newWeatherDataSubject = new NewWeatherDataSubject(weather);
        newWeatherDataSubject.addObserver(new NewUser("用户1"));
        newWeatherDataSubject.addObserver(new NewUser("用户2"));
        newWeatherDataSubject.addObserver(new NewUser("用户3"));
        log.info("温度发生改变");
        weather.setTemperature(34.f);
        newWeatherDataSubject.setWeather(weather);
        log.info("温度再次发生改变");
        weather.setTemperature(30.f);
        newWeatherDataSubject.setWeather(weather);
    }

image-20230615185508442


本章节的代码放置在 github 上:


https://github.com/yuejianli/DesignPattern/tree/develop/Observer


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/33098.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS之定位

作用&#xff1a;灵活的改变盒子在网页中的位置 实现&#xff1a; 1.定位模式&#xff1a;position 2.边偏移&#xff1a;设置盒子的位置 leftrighttopbottom 相对定位 position: relative 特点&#xff1a; 不脱标&#xff0c;占用自己原来位置显示模式特点保持不变设…

OpenStack(4)--NameSpace实现不同项目(租户)重叠网段

openstack通过namespace将不同项目&#xff08;租户&#xff09;的网络隔离&#xff0c;每个项目的管理员都需要对项目网络进行规划建设&#xff0c;这就导致不同项目之间会重复使用到某些网段&#xff0c;例如192.168.X.X就是管理员习惯使用的网段。 上一次我们新建位于vxlan…

【TCP/IP】多进程服务器的实现(进阶) - 多进程服务器模型及代码实现

经过前面的铺垫&#xff0c;我们已经具备实现并发服务器的基础了&#xff0c;接下来让我们尝试将之前的单任务回声服务器改装成多任务并发模式吧&#xff01; 多任务回声服务器模型 在编写代码前&#xff0c;先让我们大致将多任务&#xff08;回声&#xff09;服务器的模型抽象…

通过USB和wifi连接真机编写第一个脚本

目录 一、连接手机 1、通过usb数据线连接手机 2、无线连接手机 二、编写第一个脚本 一、连接手机 1、通过usb数据线连接手机 数据线连接手机并允许调试 cmd命令行执行&#xff1a; adb devices 如果没有显示device信息&#xff0c;请检查&#xff1a; 手机是否开启usb调…

配置了git config --global credential.helper store后,还是弹出输入密码框

使用http协议拉取代码时,每次pull/push都会弹出账号密码框,可以使用git的配置credential.helper来保存每次输入的账号密码到硬盘上,命令git config --global credential.helper store,store表示存到硬盘中,但是按照这样操作后git pull还是弹出密码框,通过git config --list发现…

【雕爷学编程】Arduino动手做(137)---MT8870语音解码

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

云原生之深入解析Dapr安全性之访问控制策略

一、服务调用范围访问策略 ① 跨命名空间的服务调用 Dapr 通过服务调用 API 提供端到端的安全性&#xff0c;能够使用 Dapr 对应用程序进行身份验证并设置端点访问策略&#xff1a; Dapr 应用程序可以被限定在特定的命名空间&#xff0c;以实现部署和安全&#xff0c;当然仍然…

Istio 什么是服务网格

什么是服务网格 服务网格(Service Mesh)这个术语通常用于描述构成这些应用程序的微服务网络以及应用之间的交互。随着规模和复杂性的增长&#xff0c;服务网格越来越难以理解和管理。 它的需求包括服务发现、负载均衡、故障恢复、指标收集和监控以及通常更加复杂的运维需求&am…

oracle字符集

1、查看oracle字符集 如果操作系统或者客户端的字符集设置和数据库设置不一样就会出现乱码 查询NLS_LANG即操作系统环境变量要设为 NLS_LANGUAGE_NLS_TERRITORY**.NLS_CHARACTERSET**&#xff0c;如&#xff1a; export NLS_LANG“AMERICAN_AMERICA.AL32UTF8”

Hadoop环境搭建

一、简介 1.1、概念 Hadoop是一个由Apache基金会所创建的分布式系统基础架构&#xff0c;主要解决海量数据的存储和海量数据的分析计算问题&#xff0c;从广义上来说hadoop是数据存储分包器&#xff0c;可以存储大量的数据。 1.2、优势 Hadoop具有高可靠性&#xff08;Hado…

electron+vue3+ts+vite

首先使用vite工具创建一个vue3ts的项目 npm create vite创建好vuets项目后启动项目 cd electron-vue3-ts-vitenpm installnpm run dev 访问http://127.0.0.1:5173/地址可以看到项目已经启动成功 安装Electron 接下来我们安装electron&#xff0c;使用以下命令 npm i -D el…

CV什么时候能迎来ChatGPT时刻?

卷友们好&#xff0c;我是rumor。 最近看了几篇CV的工作&#xff0c;肉眼就感受到了CVer们对于大一统模型的“焦虑”。 这份焦虑让他们开始尝试统一一切&#xff0c;比如&#xff1a; 统一复杂的自动驾驶任务的优化目标[1]&#xff0c;来自今年CVPR最佳论文。统一典型的CV任务&…

360手机 360手机刷机最高安卓版本参考

360手机 360手机刷机最高安卓版本参考 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360刷机包360手机刷机&#xff1a;360rom.github.io 【360手机(最高)安卓版本】 以下列举为常见360手机机型&#xff1b;其它早期系列&#xff0c;一般为Android4-6左右360手机UI界…

doker安装RabbitMQ以及用java连接

目录 doker安装&#xff1a; RabitMq安装&#xff1a; java链接 doker安装&#xff1a; 参考链接&#xff08;非常详细&#xff09;&#xff1a; docker安装以及部署_docker bu shuminio_春风与麋鹿的博客-CSDN博客 安装好后开启doker //启动docker服务 systemctl start do…

保偏产品系列丨5款保偏光纤产品简介

保偏光纤应用日益扩大&#xff0c;特别是在干涉型传感器等测量方面&#xff0c;利用保偏光纤的光无源器件起着非常重要的作用&#xff0c;种类也很多。 本文来介绍5款保偏光纤系列产品以及它们的性能&#xff0c;欢迎收藏转发哦&#xff01; 01、保偏光纤跳线-TLPMPC 保偏光纤跳…

梯度下降法求函数的解

题目 例如 y x^ 5 e^x3x−3&#xff0c;求解y 0的解 问题分析 首先要构造y 0的损失函数&#xff0c;让这个损失函数是凸的&#xff0c;也就是可以有最优解&#xff0c;并且是可到的&#xff0c;比较容易想到的是mse平方误差&#xff0c;我们要让y和0之间绝对误差最小。lo…

css 背景颜色级别高于背景图

<div class"bg-parent"> <img :src"employeeImg" class"bg-url" /> <div class"bg"> <el-icon class"plus-icon"> <Plus /> </el-icon> </div> </div> .bg-parent{ //父级…

python3套接字编程之socket和socketserver(TCP和UDP通信)

socket和socketserver是python3中socket通信模块&#xff0c;关于其使用做如下总结。 目录 1.socket 1.1模块引入 1.2套接字获取 1.3套接字接口 1.3.1 服务端 1.3.2 客户端套接字函数 1.3.3 公共套接字函数 1.3.4 面向锁的套接字方法 1.3.5 面向文件的套接字的函数 …

CNN卷积类型总结(标准卷积、空洞卷积、反卷积、深度可分离卷积、分组卷积等)

目录 标准卷积 卷积的运算 conv2d conv1d 其他卷积类型 空洞卷积&#xff08;膨胀卷积&#xff09; 反卷积&#xff08;转置卷积&#xff09; 深度可分离卷积 分组卷积 参考文章 上学时&#xff0c;卷积常在各个课程中出现&#xff0c;现代、信号与系统这些&#xff…

【MySQL】SQL的高阶用法

文章目录 条件查询使用关系运算符查询使用IN关键字查询使用BETWEEN AND关键字查询使用空值查询使用AND关键字查询使用OR关键字查询使用LIKE关键字查询(模糊查询)使用LIMIT分页查询使用GROUP BY进行分组查询GROUP BY和聚合函数一起使用GROUP BY和聚合函数以及HAVING一起使用 使用…