设计模式学习(二):Observer观察者模式

news2025/2/28 17:42:22

一、什么是Observer模式

在Observer模式中,当观察对象的状态发生变化时,会通知给观察者。Observer模式适用于根据对象状态进行相应处理的场景。

简单一句话概况就是:观察者会发送观察对象状态变化的通知。

二、Observer模式示例代码

下面示例程序中,observer将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不过,不同的观察者的显示方式不一样。Digitobserver会以数字形式显示数值,而Graphobserver则会以简单的图示形式来显示数值。

2.1 各个类之间的关系

先看一下所有的类和接口:

类图:

2.2 Observer接口

Observer接口是表示“观察者”的接口。具体的观察者会实现这个接口。

用于生成数值的NumberGenerator类会调用update方法,将“生成的数值发生了变化,请更新显示内容”的通知发送给Observer。

public interface Observer {
    public abstract void update(NumberGenerator generator);
}

2.3 NumberGenerator类

NumberGenerator类(代码清单17-2)是用于生成数值的抽象类。生成数值的方法( execute方法)和获取数值的方法( getNumber方法)都是抽象方法,需要子类去实现。

addObserver方法用于注册observer,而deleteobserver方法用于删除observer。

notifyObservers方法会向所有的observer发送通知,告诉它们“我生成的数值发生了变化,请更新显示内容”。该方法会调用每个observer的update方法。

public abstract class NumberGenerator {
    //用于保存所有的observer
    private ArrayList observers = new ArrayList();

    //注册observer
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    //删除observer
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    //向observer发送通知
    public void notifyObservers() {
        //把每个observer拿出来,调用他的update方法
        Iterator it = observers.iterator();
        while (it.hasNext()) {
            Observer o = (Observer) it.next();
            o.update(this);
        }
    }

    //获取数值
    public abstract int getNumber();

    //生成数值
    public abstract void execute();
}

2.4 RandomNumberGenerator类

RandomNumberGenerator类是NumberGenerator的子类,它会生成随机数。

getNumber方法用于获取number字段的值。execute方法会生成20个0 ~49的随机整数,并通过notifyObservers方法把每次生成结果通知给观察者。

public class RandomNumberGenerator extends NumberGenerator{
    //生成随机数
    private Random random  = new Random();
    //当前数值
    private int number;

    @Override
    public int getNumber() {
        return number;
    }

    @Override
    public void execute() {
        for (int i = 0; i < 20; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

2.5 DigitObserver类

DigitObserver类实现了Observer接口,它的功能是以数字形式显示观察到的数值。

它的update方法接收NumberGenerator的实例作为参数,然后通过调用NumberGenerator类的实例的getNumber方法可以获取到当前的数值,并将这个数值显示出来。

public class DigitObserver implements Observer{
    @Override
    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

2.6 GraphObserver类

GraphObserver类也实现了Observer接口。

该类会将观察到的数值以*****这样的简单图示的形式显示出来。

public class GraphObserver implements Observer{
    @Override
    public void update(NumberGenerator generator) {
        System.out.println("GraphObserver:");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}

2.7 Main测试类

Main类生成了一个RandomNumberGenerator类的实例和两个观察者,其中observer1是 DigitObserver类的实例,observer2是GraphObserver类的实例。

在使用addObserver注册观察者后,它还会调用generator.execute方法生成随机数值。

public class Main {
    public static void main(String[] args) {
        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

2.8 运行结果

部分运行结果

DigitObserver:21
GraphObserver:
*********************
DigitObserver:7
GraphObserver:
*******
DigitObserver:31
GraphObserver:
*******************************
DigitObserver:24
GraphObserver:
************************

三、拓展思路的要点

3.1 可替换性

使用设计模式的目的之一就是使类成为可复用的组件。

在Observer模式中,有带状态的ConcreteSubject角色和接收状态变化通知的ConcreteObserver角色。连接这两个角色的就是它们的接口(API ) Subject角色和 Observer 角色。

一方面RandomNumberGenerator类并不知道,也无需在意正在观察自己的(自己需要通知的对象)到底是DigitObserver类的实例还是GraphObserver类的实例。不过它知道在它的observers字段中所保存的观察者们都实现了observer接口,一定可以调用它们的update方法。

另一方面,DigitObserver类也无需在意自己正在观察的究竟是RandomNumberGenerator类的实例还是其他XxxxNumberGenerator类的实例。不过,Digitobserver类知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。

我们应该注意到这种可替换性的思想:

  • 利用抽象类和接口从具体类中抽出抽象方法

  • 在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口

这样的实现方式可以帮助我们轻松替换具体类。

3.2 从“观察”变为“通知”

Observer本来的意思是“观察者”,但实际上Observer角色并非主动地去观察,而是被动地接受来自Subject角色的通知。因此,Observer模式也被称为Publish-Subscribe(发布-订阅)模式。个人认为Publish(发布)和 Subscribe(订阅)这个名字可能更加合适。

四、相关的设计模式

4.1 Mediator中介者模式

在Mediator模式中,有时会使用Observer模式来实现Mediator角色与Colleague角色之间的通信。

就“发送状态变化通知”这一点而言,Mediator模式与Observer模式是类似的。不过,两种模式中,通知的目的和视角不同。

在 Mediator模式中,虽然也会发送通知,不过那不过是为了对Colleague角色进行仲裁而已。

而在Observer模式中,将Subject角色的状态变化通知给Observer角色的目的则主要是为了使Subject角色和 Observer角色同步。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10种顶流聚类算法Python实现(附完整代码)

目录 前言 一、聚类 二、聚类算法 三、聚类算法示例 1、库安装 2、聚类数据集 3、亲和力传播 4、聚合聚类 5、BIRCH 6、DBSCAN 7、K均值 8、Mini-Batch K-均值 9、均值漂移聚类 10、OPTICS 11、光谱聚类 12、高斯混合模型 三、总结 前言 今天给大家分享一篇关…

虚幻五引擎的出现会不会导致技术美术需求的下降?

先来结论&#xff1a;不会&#xff0c;虚幻五引擎的出现反而会致使技术美术需求的增加。 UE5主要引入的两大新技术 Nanite ​ 渲染技术Nanite&#xff0c;能够直接在游戏里表现出影视级别的超高精度模型。在演示中有一个古代雕塑的镜头&#xff0c;使用的模型超过3300万多边形…

Spring MVC阶段测试

Spring MVC阶段测试 1.新建Maven项目&#xff0c;静态资源文件&#xff0c;如JS、CSS、图片应存放在&#xff08; C &#xff09;目录下。 A、src/main/java B、src/test/java C、src/main/resources/static D、src/main/resources/templates 2.新建Maven项目&#xff0c…

无风扇工控主机支持一路CAN总线

CAN 接口如图所示&#xff0c;输入如下命令&#xff1a; ifconfig -a //查看所有网卡 如果 FlexCAN 驱动工作正常的话就会看到 CAN 对应的网卡接口&#xff0c;如图。从图中可 以看出&#xff0c;有一个名为“can0”的网卡&#xff0c;这个就是 BL302 板上的 CAN1 接口对应的 c…

Selenium用法详解【设置元素等待】【JAVA爬虫】

简介本文主要介绍如何使用java代码利用Selenium操作浏览器&#xff0c;某些网页元素加载慢&#xff0c;如何操作元素就会把找不到元素的异常&#xff0c;此时需要设置元素等待&#xff0c;等待元素加载完&#xff0c;再操作。设置元素等待很多页面都使用 ajax 技术&#xff0c;…

Python - Flask 整合 UEditor

1. 引言 UEditor 是由百度「FEX前端研发团队」开发的所见即所得富文本 web 编辑器&#xff0c;具有轻量&#xff0c;可定制&#xff0c;注重用户体验等特点&#xff0c;开源基于MIT协议&#xff0c;允许自由使用和修改代码 官方文档地址&#xff1a;http://fex.baidu.com/ued…

软件构造与与体系结构习题

软件构造与与体系结构习题第一章第二章第三章第一章 1.下面关于类的描述正确的是&#xff1f;A A、类是一组相似事物的统称。 B、一个事物可以称为一类。 C、两个完全相同的事物可以称为一类。 D、“奥巴马”和“特朗普”可以统称为“奥巴马”。 解析&#xff1a; 类&am…

【Python笔记】课时01:Python3+Pip环境配置(python安装)

1. 不同环境下安装python windows&#xff08;不好安装相关python包&#xff0c;不推荐&#xff09;anaconda&#xff08;科学计算环境 python 相关python包&#xff09;&#xff08;推荐&#xff09;linux&#xff08;如 ubuntu&#xff09;macos 2.1. windows 安装 pytho…

前端性能优化(五):webpack构建优化

目录 一&#xff1a;webpack的优化配置 1.1.Tree-shaking 1.2.JS压缩 1.3.作用域提升 1.4.Babel 优化配置 二&#xff1a;webpack的依赖优化 2.1.noParse&#xff08;不解析&#xff09; 2.2.DllPlugin 三&#xff1a;webpack 细节优化 四&#xff1a;webpack的资源压…

Linux和windows文件互传

文章目录一、方法1&#xff1a;设置共享剪切版二、方法2&#xff1a;使用FileZilla软件1.开启 Ubuntu 下的 FTP 服务2.查看Ubuntu的ip地址2.windows安装FileZilla注意一、方法1&#xff1a;设置共享剪切版 在Ubuntu20版本中好像已经自动实现 本人使用的是旧版18.4&#xff0c…

【每日十分钟前端】基础篇21,为什么HTTPS更安全(SSL原理)、单行/多行文本溢出的省略样式、DOM常见的操作有哪些

1、[HTML]为什么HTTPS更安全(SSL原理)&#xff1a;对称加密、非对称加密、摘要、数字签名、数字证书。 2、[CSS]单行/多行文本溢出的省略样式。 3、[JS]DOM常见的操作有哪些&#xff1f; 1、[HTML]为什么HTTPS更安全(SSL原理)&#xff1a; 对称加密&#xff1a;协商密钥对数据…

异常检测实战应用案例精讲-【工具篇】时序异常检测TODS

前言 时间序列异常值检测旨在识别数据中意外或罕见的实例。作为数据分析最重要的任务之一,异常值检测在时间序列数据上有多种应用,例如欺诈检测、故障检测和网络安全攻击检测。例如,雅虎 和微软 已经建立了自己的时间序列异常值检测服务来监控他们的业务数据并触发异常值…

Hudi、Iceberg底层索引Z-Order

目录 第一部分 数据库领域的Z-Order 1.1 最左匹配原则 1.2 Z-Order动机 1.3 OLTP 1.4 OLAP 第二部分 Z-Order效率分析 2.1 按照A进行查询 2.2 按照B进行查询 2.3 总结 第三部分 Z-Order缺陷 第四部分 总结及建议 参考文章 Z-Order最早是1966提出的一项将多维数据映…

数据分析-深度学习 Day1

目录&#xff1a;第一节 机器学习&深度学习介绍第二节 机器学习攻略一、机器学习的框架二、模型训练攻略三、针对Optimization Issue的优化&#xff0c;类神经网络训练不起来怎么办(一) 局部最优点和鞍点(二) 批处理和momentum(三) 自动调节学习率Learning rate(四) 损失函…

cmake的常用语法

cmake 的注释 # 注释 #[[大段注释 第二行注释 第三行注释]]cmake的log -message cmake messagemessage(arg1 arg2 arg3 arg4) # 会自动连起message 多级别输出 message(FATAL_ERROR,"abc") # 最严重的错误&#xff0c;直接停止执行 message(SEND_ERROR,"aba…

NOP+终于来了,看看蔚来随NOP+释放的数据和思考

1. 行驶数据 ADAS功能累计用户行驶里程4.9亿公里&#xff1b;NT1的NOP功能累计行驶2.3亿公里&#xff1b;NT2的Pilot功能累计行驶了1700万公里。2. 统一辅助驾驶软件架构原来NOP将直路行驶和匝道作为两个场景开发&#xff0c;场景分割思路了城区和低速就变得无穷无尽。新NOP是以…

K_A11_001 基于STM32等单片机驱动DHT11 串口与OLED0.96双显示

K_A11_001 基于STM32等单片机驱动DHT11 串口与OLED0.96双显示一、资源说明二、基本参数1.参数2.引脚说明三、驱动说明时序对应程序:四、部分代码说明1、接线说明1.1、STC89C52RCDHT11模块1.2、STM32F103C8T6DHT11模块五、基础知识学习与相关资料下载六、视频效果展示与程序资料…

锁的分类,以及锁升级原理

1. 前言 锁在并发编程中非常重要&#xff0c;但是锁的种类有点多。这边文章的目的就是为了梳理锁的分类以及 锁升级的原理。 2. 锁的分类 种类\名称synchronizedReentrantLockReentrantReadWriteLock可重入锁√√√不可重入锁乐观锁①①①悲观锁√√√公平锁√√非公平锁√√√…

javaweb-Servlet基本使用

1&#xff0c; Servlet 1.1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&#xff0c…

关于分组函数(聚合函数)

分组函数&#xff1a;也叫"多行处理函数"或"聚合函数" &#xff08;特点&#xff1a;输入多行&#xff0c;最终输出一行&#xff09; 用于对表中指定字段下的内容进行统计的函数。 - count() 计数&#xff08;返回指定字段下内容不为null的数…