8.观察者模式:思考与解读

原文地址:观察者模式:思考与解读  更多内容请关注:7.深入思考与解读设计模式

引言

在开发软件时,系统的某些状态可能会发生变化,而你希望这些变化能够自动通知到依赖它们的其他模块。你是否曾经遇到过,系统中某个对象发生了变化,但你不想让其他对象频繁地去询问这个变化,或者你不希望每次变化时都手动通知这些对象?

如果可以有一种方式,当对象的状态发生变化时,所有依赖该对象的其他对象都能自动得到通知并做出响应,这样的设计是否会让你的系统更加松耦合且易于维护?

这正是观察者模式的目的。观察者模式通过定义一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知。你是否觉得,这样的模式能够减少系统中对象之间的耦合,使得系统更加灵活?

在这篇文章中,我们将通过一系列问题,逐步引导你理解观察者模式的核心思想、应用场景以及如何实现它。

什么是观察者模式?

问题1:在软件设计中,如果有一个对象发生了变化,你通常如何通知其他依赖它的对象?

假设你有一个系统,其中有一个对象的状态变化会影响到其他多个对象。你是如何让这些依赖对象知道这个变化的?是否有一种方法,让这些对象在不询问的情况下,自动获取到最新的状态?

问题2:你是否曾经使用过“事件监听器”或“回调”机制?这些方法是否能够灵活地处理对象之间的依赖关系?

事件监听器和回调机制在某些场景下是非常有用的,但它们通常会带来强耦合。而观察者模式则能够更灵活地处理一对多的依赖关系。你是否想过,是否有一种设计模式能够解决这种问题?

观察者模式正是解决这个问题的一种有效设计模式,它通过“观察者”来自动通知依赖对象,而不需要依赖对象主动查询。

观察者模式的核心概念

问题3:观察者模式通常由哪些角色组成?每个角色的职责是什么?

观察者模式包含以下几个核心角色:

  1. 主题(Subject):被观察的对象,通常包含一个状态,当它的状态发生变化时,会通知所有注册的观察者。

  2. 观察者(Observer):依赖于主题的对象,当主题发生变化时,观察者会得到通知并进行相应的处理。

  3. 具体主题(ConcreteSubject):实现了主题接口,维护观察者列表,并在状态变化时通知它们。

  4. 具体观察者(ConcreteObserver):实现了观察者接口,响应主题的变化。

你能理解这些角色是如何相互协作,确保在主题变化时,观察者能够自动得到通知吗?

问题4:在观察者模式中,主题与观察者是如何解耦的?它们是如何通过某种机制进行通信的?

观察者模式的关键是,主题不需要知道具体有哪些观察者,它只需要维护一个观察者的列表,并在状态变化时通知所有的观察者。你能理解,这样的设计是如何让系统中的各个部分更加解耦的?

观察者模式的实现

让我们通过一个简单的例子来理解观察者模式的实现。假设你正在开发一个天气预报系统,系统中有多个展示天气信息的应用(例如,手机应用、网页应用等),当天气数据发生变化时,所有应用都应该被通知并更新显示的内容。

步骤1:定义观察者接口

from abc import ABC, abstractmethod# 观察者接口
class Observer(ABC):@abstractmethoddef update(self, temperature: float, humidity: float):pass

问题5:观察者接口(Observer)定义了哪些方法?为什么需要一个统一的接口来保证所有观察者的行为一致?

观察者接口定义了一个update()方法,所有观察者都必须实现这个方法。你是否理解,为什么这种方式能够确保所有观察者在状态变化时都能得到统一的通知,并进行相应的处理?

步骤2:定义主题接口
class Subject(ABC):@abstractmethoddef register_observer(self, observer: Observer):pass@abstractmethoddef remove_observer(self, observer: Observer):pass@abstractmethoddef notify_observers(self):pass

问题6:主题接口(Subject)需要提供哪些方法来管理观察者?它为什么需要register_observer()remove_observer()notify_observers()方法?

主题接口通过register_observer()remove_observer()notify_observers()来管理观察者列表。你是否理解,为什么这些方法是观察者模式的核心?它们如何帮助主题管理观察者,并在状态变化时通知所有观察者?

步骤3:定义具体主题类
class WeatherStation(Subject):def __init__(self):self._observers = []self._temperature = 0.0self._humidity = 0.0def register_observer(self, observer: Observer):self._observers.append(observer)def remove_observer(self, observer: Observer):self._observers.remove(observer)def notify_observers(self):for observer in self._observers:observer.update(self._temperature, self._humidity)def set_weather_data(self, temperature: float, humidity: float):self._temperature = temperatureself._humidity = humidityself.notify_observers()  # 当天气数据变化时通知所有观察者

问题7:具体主题类(WeatherStation)如何管理观察者,并在状态变化时通知它们?

WeatherStation类维护一个观察者列表,并通过notify_observers()方法通知所有观察者。你是否能理解,为什么我们通过set_weather_data()方法来触发状态变化,并通知所有的观察者?

步骤4:定义具体观察者类
class PhoneApp(Observer):def update(self, temperature: float, humidity: float):print(f"Phone App: Weather updated! Temperature: {temperature}°C, Humidity: {humidity}%")class WebApp(Observer):def update(self, temperature: float, humidity: float):print(f"Web App: Weather updated! Temperature: {temperature}°C, Humidity: {humidity}%")

问题8:具体观察者类(如PhoneAppWebApp)是如何响应主题的变化的?它们为什么需要实现update()方法?

具体观察者类实现了update()方法,并在该方法中处理主题状态变化后的逻辑(如更新显示)。你是否理解,为什么观察者需要根据主题的变化来更新自己的状态,而这种更新是自动触发的?

步骤5:客户端代码
def main():weather_station = WeatherStation()phone_app = PhoneApp()web_app = WebApp()weather_station.register_observer(phone_app)weather_station.register_observer(web_app)weather_station.set_weather_data(25.5, 60)  # 模拟天气数据变化weather_station.set_weather_data(30.0, 65)  # 再次模拟天气数据变化if __name__ == "__main__":main()

问题9:在客户端代码中,如何通过主题对象来注册观察者,并触发通知?当天气数据发生变化时,如何确保所有观察者都能接收到通知?

客户端通过register_observer()方法注册观察者对象,当天气数据变化时,WeatherStation通过调用notify_observers()通知所有注册的观察者。你是否理解,这种机制如何保证了在状态变化时,所有依赖对象(观察者)都能自动响应?

观察者模式的优缺点

问题10:观察者模式的优点是什么?它如何帮助我们解耦系统中的不同模块?

观察者模式通过将主题与观察者解耦,避免了直接依赖关系,使得系统更加灵活和可扩展。你是否理解,这种设计如何帮助你在不修改主题类的情况下,轻松增加新的观察者?同时,观察者也不需要知道主题类的具体实现。

问题11:观察者模式的缺点是什么?它在某些情况下是否会导致性能问题或过度通知?

虽然观察者模式非常灵活,但在某些情况下,当有大量观察者时,可能会导致性能问题。你是否认为,如果观察者数量过多,通知的效率可能成为瓶颈?或者,是否可能存在不必要的通知?

适用场景

问题12:你能想到哪些场景,观察者模式能够发挥作用?

观察者模式适用于以下场景:

  • 当多个对象依赖于某个对象的状态变化时。

  • 当你需要将事件驱动机制引入系统时。

你能想到其他场景吗?例如,用户界面的事件监听、股票市场的价格更新等,是否也可以使用观察者模式?

问题13:观察者模式是否适用于所有场景?在某些情况下,是否有更合适的设计模式?

观察者模式适用于需要多方监听和响应的场景,但如果对象之间的依赖较少,或者没有动态更新的需求,是否可以使用更简单的设计模式?

接下来,我们将通过具体的代码示例来加深理解观察者模式。

观察者模式深入解读

一、引言

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。观察者模式通常用于事件处理系统,比如用户界面、消息通知系统等。


二、简单理解:什么是观察者模式?

1. 什么是观察者模式?

观察者模式的核心思想是:当一个对象的状态发生改变时,所有依赖于它的对象都会自动得到通知并更新。这个模式常用于一种场景:当数据发生变化时,其他对象需要得到变化的通知并作出响应。

通俗地讲,观察者模式就像是你订阅了一家新闻网站,每当该网站发布了新的新闻,你就会收到通知。你不需要主动去检查网站是否有新新闻,而是当新闻发生变化时,系统会自动通知你。

2. 观察者模式的组成部分

观察者模式通常包含以下几个部分:

  • 主题(Subject):维护一组观察者,并在状态变化时通知所有观察者。

  • 观察者(Observer):定义一个更新接口,供主题在状态发生变化时调用。

  • 具体主题(ConcreteSubject):实现主题接口,保存状态,并在状态变化时通知观察者。

  • 具体观察者(ConcreteObserver):实现观察者接口,根据主题的状态变化做出响应。


三、用自己的话解释:如何理解观察者模式?

1. 类比实际生活中的场景

假设你是一家杂志的订阅者,每当这本杂志出版新一期时,你都会收到邮件通知。这时候,你是“观察者”,杂志社是“主题”,每当杂志社发布新内容时,它会通知所有订阅的读者。

在编程中,观察者模式通常应用于一对多的关系,当一个对象的状态变化时,其他对象会被通知并自动更新。比如,用户界面中一个输入框的值发生变化时,其他依赖于这个输入框值的组件(如显示结果的标签、搜索框等)会自动更新。

2. 为什么要使用观察者模式?

观察者模式的主要优势在于,它通过松耦合的方式管理对象之间的依赖关系。观察者与主题之间没有直接的连接,主题只知道观察者的接口,不知道具体的实现,这使得系统更加灵活和可扩展。


四、深入理解:观察者模式的实现

接下来,我们通过一个具体的代码示例来实现观察者模式,帮助你更好地理解如何在代码中使用这个模式。

示例:新闻推送系统

假设我们要开发一个新闻推送系统,用户可以订阅不同的新闻类型。当有新的新闻发布时,所有订阅该类型的用户都会收到通知。

1. 定义观察者接口
# 观察者接口:定义更新方法
class Observer:def update(self, message: str):pass
2. 定义主题接口
# 主题接口:定义注册、移除观察者的方法
class Subject:def register_observer(self, observer: Observer):passdef remove_observer(self, observer: Observer):passdef notify_observers(self):pass
3. 定义具体主题类:新闻发布
# 具体主题类:新闻发布
class NewsPublisher(Subject):def __init__(self):self._observers = []self._latest_news = ""def register_observer(self, observer: Observer):self._observers.append(observer)def remove_observer(self, observer: Observer):self._observers.remove(observer)def notify_observers(self):for observer in self._observers:observer.update(self._latest_news)def set_latest_news(self, news: str):self._latest_news = newsself.notify_observers()  # 状态变化时通知所有观察者
4. 定义具体观察者类:用户
# 具体观察者类:用户
class User(Observer):def __init__(self, name: str):self._name = namedef update(self, message: str):print(f"{self._name} received news: {message}")
5. 客户端代码:订阅新闻
# 创建新闻发布者实例
news_publisher = NewsPublisher()# 创建用户实例
user1 = User("Alice")
user2 = User("Bob")# 用户订阅新闻
news_publisher.register_observer(user1)
news_publisher.register_observer(user2)# 发布新闻
news_publisher.set_latest_news("Breaking: New python version released!")# 取消某个用户的订阅
news_publisher.remove_observer(user2)# 再次发布新闻
news_publisher.set_latest_news("Update: Python tutorial available!")
代码解析:
  1. Observer 类:这是观察者接口,定义了 update 方法,主题会通过这个方法通知观察者更新。

  2. Subject 类:这是主题接口,定义了 register_observerremove_observer 和 notify_observers 方法,用来管理观察者的注册、移除和通知。

  3. NewsPublisher 类:这是具体的主题类,继承了 Subject,并实现了管理观察者和通知更新的逻辑。当有新的新闻发布时,它会调用 notify_observers 方法,通知所有注册的观察者。

  4. User 类:这是具体的观察者类,继承了 Observer,当主题状态变化时,通过 update 方法接收到新新闻的通知。

  5. 客户端代码:创建新闻发布者和多个用户,用户订阅新闻发布,发布者通知所有订阅的用户更新。


五、解释给别人:如何讲解观察者模式?

1. 用简单的语言解释

观察者模式就像是你订阅了一个新闻频道,每当有新新闻发布时,你都会收到通知。你不需要时刻查看新闻网站,而是当新闻变化时,你会自动收到更新。它让你能跟随某个变化的对象,而不用频繁地查询该对象。

2. 为什么要使用观察者模式?

使用观察者模式的好处是,它让系统的各个组件之间通过接口解耦。主题和观察者之间没有直接的依赖,主题只知道观察者的接口,并且通过通知机制来更新观察者。当有新观察者加入或离开时,主题不需要做太多改动,从而提高了系统的灵活性和扩展性。


六、总结

通过一系列问题的引导,我们逐步理解了观察者模式的核心思想和实现方式。观察者模式通过定义一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖对象能够自动接收到通知并做出响应。它让系统更加松耦合,灵活且可扩展。

通过以上过程,我们可以得出以下结论:

  • 观察者模式 是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。

  • 它的主要优点是解耦、灵活性和扩展性,尤其适用于事件驱动或消息推送的场景。

  • 观察者模式的应用非常广泛,例如 GUI 组件、事件处理、推送通知系统等。

观察者模式的优点:
  • 松耦合:观察者和主题之间没有直接依赖,便于扩展和维护。

  • 扩展性:可以灵活地增加或移除观察者,而不影响主题。

  • 易于实现:适合实现一对多的通信关系,尤其在实时更新场景中非常有用。

观察者模式的缺点:
  • 可能导致性能问题:如果观察者数量非常多,可能会影响通知效率。

  • 依赖复杂:如果观察者和主题之间有太多复杂的依赖,可能会导致系统的理解和维护难度增加。

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

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

相关文章

【HD-RK3576-PI】Ubuntu桌面多显、旋转以及更新Logo

硬件:HD-RK3576-PI 软件:Linux6.1Ubuntu22.04 在基于HD-RK3576-PI硬件平台运行Ubuntu 22系统的开发过程中,屏幕方向调整是提升人机交互体验的关键环节。然而,由于涉及uboot引导阶段、内核启动界面、桌面环境显示全流程适配&#x…

Rsync+sersync2实现目录实时同步

Sersync rsync 实现实时同步服务 sersync2二进制包目录规划 /app/tools/sersync/ /app/tools/sersync/bin /app/tools/sersync/conf项目架构是这样的: ------------------- ------------------- ------------------- | | …

MySQL视图高级应用与最佳实践

1. 视图与索引的协同优化​​ ​​物化视图(模拟实现)​​ MySQL原生不支持物化视图,但可通过“定时刷新”的物理表模拟: -- 1. 创建存储结果的物理表 CREATE TABLE cached_monthly_sales (product_id INT,total_sales DECIMAL(10…

string的模拟实现 (6)

目录 1.string.h 2.string.cpp 3.test.cpp 4.一些注意点 本篇博客就学习下如何模拟实现简易版的string类&#xff0c;学好string类后面学习其他容器也会更轻松些。 代码实现如下&#xff1a; 1.string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include <…

Unity:像素(Pixels) 和 单位(Units)

目录 从第一性原理出发&#xff1a;什么是像素和 Unit&#xff1f; &#x1f9f1; 1. 像素&#xff08;Pixel&#xff09;&#xff1a;图像的最小单位 &#x1f4d0; 2. Unity Unit&#xff08;单位&#xff09;&#xff1a;游戏世界中的度量单位 核心换算公式&#xff1a;…

【失败总结】Win10系统安装docker

1.启用或关闭windows功能中&#xff0c;将Hyper-V功能勾选全部启用&#xff0c;容器勾选。设置好后要重启电脑。 2.管网下载下载安装Docker  Docker官网&#xff1a;https://www.docker.com/ 3.可以自定义Docker安装路径 新建安装目录&#xff1a;d:\MySoftware\Docker并将D…

《Adaptive Layer-skipping in Pre-trained LLMs》- 论文笔记

作者&#xff1a;Xuan Luo, Weizhi Wang, Xifeng Yan Department of Computer Science, UC Santa Barbara xuan_luoucsb.edu, weizhiwangucsb.edu, xyancs.ucsb.edu 1. 引言与动机 1.1 背景 LLM 的成功与挑战: 大型语言模型 (LLMs) 在翻译、代码生成、推理等任务上取得巨大成…

DQN在Gym的MountainCar环境的实现

DQN on MountainCar 引言 在本次实验里&#xff0c;我构建了DQN和Dueling DQN&#xff0c;并在Gymnasium库的MountainCar环境中对它们展开测试。我通过调整训练任务的超参数&#xff0c;同时设计不同的奖励函数及其对应参数&#xff0c;致力于获取更优的训练效果。最后&#…

计算机网络综合实验指南

计算机网络综合实验指南 本实验将结合《计算机网络自顶向下》前三章的核心概念&#xff0c;通过实际操作加深对应用层、运输层和网络层的理解。实验涵盖 HTTP/TCP抓包分析、DNS解析观察、网页性能评估及简单Socket编程&#xff0c;帮助你将理论转化为实践。 实验准备 工具&…

【AI部署】腾讯云GPU-RUN—SadTalker的AI数字人视频—未来之窗超算中心

磁盘空间 创建未来之窗 查看磁盘命令 df -h 指定路径创建环境 conda create --prefix sadtalker python3.10 指令路径运行环境 conda activate ./sadtalker 安装环境 pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://…

爬虫利器SpiderTools谷歌插件教程v1.0.0!!!web端JavaScript环境检测!!!

SpiderTools谷歌插件教程v1.0.0 一、SpiderTools简介二、下载通道三、插件介绍四、插件使用五、工具函数使用 补环境工具推荐&#xff1a;爬虫补环境利器webEnv 一、SpiderTools简介 SpiderTools主要用于检测和监控网页的JavaScript运行环境。该插件可以帮助开发者更好地查看…

Android开发协调布局滑动悬停

Android开发协调布局滑动悬停 直接给个xml,防止下次忘了怎么写。 <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"x…

Linux学习——TCP

一.TCP编程API 1.socket函数 1.socket函数 include include int socket(int domain,int type,int protocol); 参数 domain AF_INET AF_INET6 AF_UNIX,AF_LOCAL AF_NETLINK AF_PACKET type SOCK_STREAM: 流式…

Linux驱动开发--异步通知与异步I/O

3、异步通知与异步I/O 3.1 Linux信号 阻塞与非阻塞访问、poll()函数提供了较好的解决设备访问的机制&#xff0c;但是如果有了异步通知&#xff0c;整套机制则更加完整了。 异步通知的意思是&#xff1a;一旦设备就绪&#xff0c;则主动通知应用程序&#xff0c;这样应用程序…

大语言模型推理能力的强化学习现状理解GRPO与近期推理模型研究的新见解

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【Linux系统】Linux基础指令(详解Linux命令行常用指令,每一个指令都有示例演示)

文章目录 一、与文件路径相关的指令0.补充知识&#xff1a;路径的认识1.pwd 指令2.cd 指令&#xff08;含家目录的介绍&#xff09; 二、创建和删除文件的指令0.补充知识&#xff1a;普通文件和目录文件1.touch 指令&#xff08;可以修改文件的时间戳&#xff09;2.mkdir 指令3…

LangChain 单智能体模式示例【纯代码】

# LangChain 单智能体模式示例import os from typing import Anyfrom langchain.agents import AgentType, initialize_agent, Tool from langchain_openai import ChatOpenAI from langchain.tools import BaseTool from langchain_experimental.tools.python.tool import Pyt…

解决:VSCode C++ conan 安装第三方库后 头文件报错

文章目录 1 头文件include路径查找报错参考 1 头文件include路径查找报错 找到conan_toolchain.cmake中 INCLUDE_PATH list(PREPEND CMAKE_INCLUDE_PATH "/Users/hanliqiang/.conan2/p/b/fmte8c4f7a755477/p/include")生成C编译配置 CtrlShiftP 中选择C Edit Confi…

松灵Cobot Magic双臂具身遥操机器人(基于ROS的定位建图与协同导航技术)

摘要 本文以CobotMagic可移动协作机器人为研究对象&#xff0c;从硬件架构设计、软件系统架构、多传感器融合定位建图系统、智能导航系统协同机制四个维度&#xff0c;深入解析机器人系统工作原理。重点研究多传感器融合定位建图系统实现原理&#xff0c;结合实测数据验证系统…

回归,git 分支开发操作命令

核心分支说明 主分支&#xff08;master/production&#xff09;存放随时可部署到生产环境的稳定代码&#xff0c;仅接受通过测试的合并请求。 开发分支&#xff08;develop&#xff09;集成所有功能开发的稳定版本&#xff0c;日常开发的基础分支&#xff0c;从该分支创建特性…