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,一经查实,立即删除!

相关文章

【JS笔记】JavaScript语法 《基础+重点》 知识内容,快速上手(一)

JavaScript基础语法 HTML &#xff1a;标记语言JavaScript &#xff1a;编程语言&#xff08;脚本&#xff09; 序言 JavaScript发展历史&#xff08;JS&#xff09; 1. 1994年&#xff0c;网景公司(Netscape)发布了Navigator浏览器0.9版&#xff0c;这是世界上第一款比较成…

Mybatis Java API - SqlSessionFactoryBuilder

在MyBatis中&#xff0c;用于与数据库进行交互的主要Java接口是SqlSession。通过这个接口&#xff0c;您可以执行命令、获取映射器并管理事务。稍后我们将更详细地讨论SqlSession本身&#xff0c;但首先我们必须学习如何获取SqlSession的实例。SqlSession是由SqlSessionFactory…

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…

软件集成测试

软件集成测试是将各个独立的软件模块组合起来&#xff0c;并测试它们之间的接口和交互是否正常工作的过程。下面是软件集成测试的一般步骤&#xff1a; 确定测试策略&#xff1a;确定集成测试的目标、范围和测试策略。确定要测试的软件模块和它们之间的依赖关系。 设计测试用例…

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

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

基于关键点的人脸对齐方法

人脸旋转校正的一般步骤&#xff1a; 1.人脸检测&#xff1a;首先使用人脸检测算法来检测图像中的人脸位置。 2.人脸关键点检测&#xff1a;对于每张检测到的人脸&#xff0c;使用人脸关键点检测算法来检测人脸中的关键点&#xff0c;如眼睛、鼻子、嘴巴等。 &#xff08;项目…

【leetcode100-026】【链表/快慢指针】环形链表II

【题干】 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统…

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;也能通过绘画模型生成想…

基于SpringBoot的线上学习资源智能推荐系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的线上学习资源智能推荐系…

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

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

Hystrix相关面试题及答案

1、什么是Hystrix&#xff0c;它是如何工作的&#xff1f; Hystrix是一个由Netflix开源的库&#xff0c;主要用于在分布式系统中提供延迟和容错功能&#xff0c;通过阻止服务故障的蔓延和提供回退机制来保护系统。它在服务架构中扮演着重要的角色&#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 为什么自己编译内核…