Head First Design Patterns - 观察者模式

观察者模式

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式是一种对象行为型模式

场景

  • 很多用户都订阅了某一公众号,当该公众号更新时,所以用户都会收到消息。该公众号叫做【主题,Subject】,订阅者叫做【观察者,Observer】。
  • 气象台会将每日更新的天气数据,如温度,气压等,下发给第三方的网站进行显示。气象台被称为【主题,Subject】,不同的第三方网站叫做【观察者,Observer】。书上也是以气象台为例。
    在这里插入图片描述

角色

  • 主题(Subject):指被观察的对象。可以为接口,也可以为抽象类,书中的例子为接口。该接口中一般定义了registerObserver()、removeObserver()、notifyObservers()等方法,作用分别为观察者注册、移除观察者、通知所有的观察者。该类中还会维护一个数据结构来保存所有观察者的引用,书中例子用了List<>,但为了线程安全,一般都用vector<>
  • 具体主题(ConcreteSubject):因为书中的主体定义为接口,因此具体主题类是实现了该主题接口。具体主题也会定义自己的一些逻辑。
  • 观察者(Observer):为接口。定义了update()方法,主题类调用该方法将消息通知到观察者。
  • 具体观察者(ConcreteObserver):维护了一个具体主题对象的引用,方便利用主题的方法进行注册等工作。具体观察者作为Observer的实现类,还实现了update方法。

上述角色可以参考后面的类图。

气象台的实现

气象台的改变逻辑会放在measurementsChanged方法中。

不用设计模式的实现

public class WeatherData {public void measurementsChanged() {float temp = getTemperature();float humidity = getHumidity();float pressure = getPressure();// 可以理解为更新不同接入气象台网站的显示currentConditionDisplay.update(temp, humidity, pressure);statisticsDisplay.update(temp, humidity, pressure);}
}

以上实现违背了一些设计原则:

  1. 是针对具体的实现,而不是针对接口的实现。没有办法在不修改代码的情况下添加或者移除一些显示元素。
  2. update方法中都是传入几个相同参数,属于主体统一推到观察者的模式没有考虑到网站真正需要什么
  3. 属于硬编码

观察者模式的实现

类图

在这里插入图片描述

示例

  1. 定义主题接口
public interface Subject {public void registerObserver(Observer o);public void removeObserver(Observer o);public void notifyObserver();}
  1. 实现具体的主题
public class WeatherData implements Subject{List<Observer> observerList;private float temp; //温度private float humidity; //湿度private float pressure; //压强public WeatherData() {observerList = new ArrayList<Observer>();}@Overridepublic void registerObserver(Observer o) {observerList.add(o);}@Overridepublic void removeObserver(Observer o) {observerList.remove(o);}@Overridepublic void notifyObserver() {observerList.forEach(Observer::update);}public void measurementsChanged() {notifyObserver();}public void setMeasurements(float temperatue, float humidity, float pressure) {this.temp = temperatue;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public float getTemp() {return temp;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}
}
  1. 定义观察者接口
public interface Observer {void update();
}
  1. 定义展示接口(书中业务的实现,与设计模式没有联系)
public interface DisplayElement {public void display();
}
  1. 定义具体的观察者
public class CurrentConditionDisplay implements Observer, DisplayElement{private float temp;private float humidity;private float pressure;private WeatherData weatherData;public CurrentConditionDisplay(WeatherData weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update() {this.temp = weatherData.getTemp();this.humidity = weatherData.getHumidity(); // 观察者需要什么,可以从主题中拉取,是属于拉的模式this.pressure = weatherData.getPressure();this.display();}@Overridepublic void display() {System.out.println("current display:" +  temp + " " + humidity + " " + pressure);}
  1. 客户端
 public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionDisplay currentConditionDisplay =new CurrentConditionDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);}

输出:
current display:80.0 65.0 30.4 // 例子中只定义了一个观察者

观察者模式所涉及到的设计原则

  • 封装变化。该例子中,变化的是主题的状态、观察者的数量和类型。
  • 针对接口编程而不是针对实现编程。主题跟踪具体的观察者,而观察者通过主题接口来注册并通知。
  • 松耦合。降低了主题和观察者之间的耦合关系,使代码更能应对场景的变化。

应用

spring中的事件驱动模型,具体参考观察者模式

参考文章
观察者模式
设计模式前传

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

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

相关文章

C++17中的内联变量

在C11中&#xff1a; (1).声明为constexpr的函数隐式地是内联函数; (2).deleted函数隐式地是一个内联函数。 在内联函数中&#xff1a; 1.所有函数定义中的函数局部静态对象(function-local static object)在所有翻译单元之间共享(它们都引用一个翻译单…

【c++————————构造函数和析构函数】

【c————————构造函数和析构函数】 欢迎阅读新一期的c模块————构造函数和析构函数 ✒️个人主页&#xff1a;-Joker- &#x1f3f7;️专栏&#xff1a;C &#x1f4dc;代码仓库&#xff1a;c_code &#x1f339;&#x1f339;欢迎大佬们的阅读和三连关注&#xff0c…

Dependency Track:智能组件分析平台。

Dependency Track:智能组件分析平台。 ############################# 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习,切勿用于非法犯罪活动,对于恶意使用该工具造成的损失,和本人及开发者无关。 ################…

Linux | 解决问题Ubuntu重启无法进入系统以及网络无法连接【图文详解】

Ubuntu18.04重启无法进入系统&#xff0c;重开后如图 一直在加载系统内核4.15.0-213-generic,无法加载 错误原因 原本的系统是Ubuntu16.04,使用命令升级到Ubuntu18.04版本&#xff0c;升级重启后&#xff0c;远程无法连接&#xff01; 错误解决 第一步&#xff1a;进入GRUB…

AIGC入门系列1:感性的认识扩散模型

1、序言 大家好&#xff0c;欢迎来到AI手工星的频道&#xff0c;我是专注AI领域的手工星。AIGC已经成为AI又一个非常爆火的领域&#xff0c;并且与之前的AI模型不同&#xff0c;AIGC更适合普通人使用&#xff0c;我们不仅可以与chatgpt对话&#xff0c;也能通过绘画模型生成想…

使用ASP.NET MiniAPI 调试未匹配请求路径

本文将介绍如何在使用ASP.NET MiniAPI时调试未匹配到的请求路径。我们将详细讨论使用MapFallback方法、中间件等工具来解决此类问题。 1. 引言 ASP.NET MiniAPI是一个轻量级的Web API框架&#xff0c;它可以让我们快速地构建和部署RESTful服务。然而&#xff0c;在开发过程中如…

PACC:数据中心网络的主动 CNP 生成方案

PACC&#xff1a;数据中心网络的主动 CNP 生成方案 文章目录 PACC&#xff1a;数据中心网络的主动 CNP 生成方案PACC算法CNP数据结构PACC参数仿真结果参考文献 PACC算法 CNP数据结构 PACC参数 仿真结果 PACC Hadoop Load0.2 的情况&#xff1a; PACC Hadoop Load0.4 的情况&a…

go slice源码探索(切片、copy、扩容)和go编译源码分析

文章目录 概要一、数据结构二、初始化2.1、字面量2.2、下标截取2.2.1、截取原理 2.3、make关键字2.3.1、编译时 三、复制3.1、copy源码 四、扩容4.1、append源码 五&#xff1a;切片使用注意事项六&#xff1a;参考 概要 Go语言的切片&#xff08;slice&#xff09;是对数组的…

axios的使用及说明

目录 1.说明 2.直接使用 3.封装使用 4.注意 1.说明 官网&#xff1a;Axios 实例 | Axios中文文档 | Axios中文网 Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使…

Java超高精度无线定位技术--UWB (超宽带)人员定位系统源码

UWB室内定位技术是一种全新的、与传统通信技术有极大差异的通信新技术。它不需要使用传统通信体制中的载波&#xff0c;而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据&#xff0c;从而具有GHz量级的带宽。 UWB&#xff08;超宽带&#xff09;高精度定位系统是一…

java零拷贝zero copy MappedByteBuffer

目录 调用操作系统的 mmap 未使用 mmap 的文件通过网络传输的过程 使用 mmap 的文件通过网络传输的过程 使用例子 调用操作系统的 sendfile() 在 java 中的具体实现 mmap的优劣 mmap 的不足 mmap 的优点 mmap 的使用场景 对于零拷贝&#xff08;zero copy&#xff09…

C语言实验4:指针

目录 一、实验要求 二、实验原理 1. 指针的基本概念 1.1 指针的定义 1.2 取地址运算符&#xff08;&&#xff09; 1.3 间接引用运算符&#xff08;*&#xff09; 2. 指针的基本操作 2.1 指针的赋值 2.2 空指针 3. 指针和数组 3.1 数组和指针的关系 3.2 指针和数…

【Linux】内核编译 镜像制作

文章目录 一、Ubuntu内核编译1.1 为什么自己编译内核1.2 Ubuntu 内核源码下载1.21 内核的作用1.22 Linux内核与ubuntu内核1.23 Ubuntu内核源码获取 1.3 在Windows系统下编译ubuntu内核1.4 在Linux系统下编译ubuntu内核 二、镜像制作 一、Ubuntu内核编译 1.1 为什么自己编译内核…

用LCD循环右移显示“Welcome to China“

#include<reg51.h> //包含单片机寄存器的头文件 #include<intrins.h> //包含_nop_()函数定义的头文件 sbit RSP2^0; //寄存器选择位&#xff0c;将RS位定义为P2.0引脚 sbit RWP2^1; //读写选择位&#xff0c;将RW位定义为P2.1引脚 sbit EP2^2; //使能…

Debezium日常分享系列之:向 Debezium 连接器发送信号

Debezium日常分享系列之&#xff1a;向 Debezium 连接器发送信号 一、概述二、激活源信号通道三、信令数据集合的结构四、创建信令数据集合五、激活kafka信号通道六、数据格式七、激活JMX信号通道八、自定义信令通道九、Debezium 核心模块依赖项十、部署自定义信令通道十一、信…

【C# 技术】 C# 常用排序方式——自定义数据排序

C# 常用排序方式——自定义数据排序 前言 在最近的项目中经常会对C#中的数据进行排序&#xff0c;对于基本数据类型&#xff0c;其排序方式比较简单&#xff0c;只需要调用内置算法即可实现&#xff0c;但对于自定义数据类型以及自定义排序规则的情况实现起来就比较麻烦&…

区分LR(0),SLR(1),LR(1)和LALR(1)

目录 对于LR(0)文法&#xff1a; 对于SLR(1)文法&#xff1a; 对于LR(0)和SLR(1)文法&#xff1a; 对于LR(1)和SLR(1)文法&#xff1a; 对于LALR(1)文法&#xff1a; 例题1&#xff1a; 例题2&#xff1a; 例题3&#xff1a; 例题4&#xff1a; 这几个文法大致的步骤都…

推荐几个贼有意思的开源项目!

这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、Java、Go、C/C、Swift...让你在短时间内感受到开源的魅力&#xff0c;对编程产生兴趣&#xff01; C 项目 1、kilo&#xff1a;不到 1 千行代码实现的迷你文本编辑器。该项…

Portraiture4.1汉化版PS磨皮插件(支持原生m1芯片m2)

Portraiture汉化版PS磨皮插件。本期推荐一款全新ai算法ps2024中文汉化版ps磨皮插件Portraiture 4.1.2美颜滤镜安装包最新版ps调整肤色插件! 全新Portraiture 4.1.2版本PS人像修图美颜磨皮插件&#xff0c;升级AI算法&#xff0c;并支持多人及全身磨皮美化模式&#xff0c;推荐…

【嵌入式开发 Linux 常用命令系列 7.3 -- linux 命令行数值计算】

文章目录 linux 命令行数值计算使用 awk使用 bc 命令使用 Bash 的内置算术扩展使用 expr脚本命令实现 linux 命令行数值计算 在 Linux 命令行中&#xff0c;您可以使用多种方法来执行基本的数学运算。以下是一些示例&#xff1a; 使用 awk awk 是一个强大的文本处理工具&…