设计模式-观察者模式(Observer Pattern)结构|原理|优缺点|场景|示例

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,允许一个或多个观察者对象订阅主题对象,当主题对象状态发生改变时,会通知所有已订阅的观察者对象,使得它们能够自动更新自己。

主要角色:

  1. Subject(抽象主题、被观察者)

    • 提供了一个注册和移除观察者的接口方法(如 registerObserver() 和 removeObserver())。
    • 定义了一个通知所有观察者的接口方法(如 notifyObservers())。
  2. ConcreteSubject(具体主题、具体被观察者)

    • 继承自抽象主题,是抽象主题的具体实现。
    • 维护一个观察者集合,并在自身状态改变时调用 notifyObservers() 方法通知所有观察者。
    • 可能会提供获取当前状态的方法。
  3. Observer(抽象观察者)

    • 定义了一个更新接口,所有具体的观察者都要实现这个接口。
    • 提供了当主题状态变化时需要执行的更新方法(如 update())。
  4. ConcreteObserver(具体观察者)

    • 是Observer接口的具体实现,包含了指向具体主题的引用。
    • 在接收到主题的通知后,通过调用 update() 方法响应状态的变化,更新自身的状态

工作流程:

  1. 观察者通过 registerObserver() 方法注册到被观察者。
  2. 当被观察者状态发生改变时,调用 notifyObservers() 方法。
  3. 被观察者遍历观察者集合,调用每个观察者的 update() 方法。
  4. 观察者在其 update() 方法中根据主题提供的信息或者直接访问主题的状态来进行相应的更新操作。

优缺点:

优点:

  1. 松耦合:观察者模式实现了主题(被观察者)和观察者之间的松耦合。主题不需要知道观察者的具体类型,只需要通过一个通用的接口进行通知,这有助于降低模块间的耦合度,提高系统的灵活性和可维护性。

  2. 扩展性好:增加新的观察者或删除已有的观察者都很方便,只需修改具体的观察者类,对主题和其他观察者没有影响。

  3. 响应式编程友好:观察者模式可以很容易地构建基于事件的系统,使得系统具备事件驱动能力,能够及时响应状态变化。

缺点:

  1. 如果观察者过多,每次主题状态变化时都需要通知所有的观察者,可能会造成效率低下,尤其是在大型系统中,通知机制可能成为瓶颈。

  2. 如果观察者和主题之间存在循环依赖,可能导致系统难以理解和调试。

  3. 如果没有良好的设计,可能会出现观察者忘记取消注册而导致内存泄露的情况。

  4. 当主题类的状态频繁变化时,会导致大量无谓的通知发送,加大系统的负载。

  5. 对于观察者来说,可能会因为不清楚主题的状态何时发生变化,而过度依赖于外部通知,不利于逻辑的清晰性和独立性。

 应用场景:

  • 在GUI编程中,按钮点击事件通知多个组件更新界面。
  • 数据绑定场景,数据模型变化时自动更新视图层。
  • 网络通信,服务器状态改变时通知所有订阅的客户端。
  • 日志框架中,日志级别变更时重新配置所有相关的日志处理器。

代码示例(以Java为例)

// 抽象观察者接口
public interface Observer {// 更新方法,当被观察者状态改变时会被调用void update(String message);
}// 抽象被观察者接口
public interface Subject {// 注册观察者void registerObserver(Observer observer);// 移除观察者void removeObserver(Observer observer);// 通知所有观察者void notifyObservers(String message);
}// 具体被观察者类
public class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String message) {for (Observer observer : observers) {observer.update(message);}}// 被观察者的状态发生变化时,触发通知public void changeState(String newState) {System.out.println("Subject state changed to: " + newState);notifyObservers(newState);}
}// 具体观察者类
public class ConcreteObserver implements Observer {private String name;public ConcreteObserver(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " received the following message: " + message);}
}// 测试类
public class Main {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observer1 = new ConcreteObserver("Observer 1");Observer observer2 = new ConcreteObserver("Observer 2");subject.registerObserver(observer1);subject.registerObserver(observer2);subject.changeState("New State");  // 改变被观察者状态,此时会触发通知}
}

在这个示例中,ConcreteSubject 类是具体的被观察者,它管理着一个观察者列表。当其状态改变时,会调用 notifyObservers() 方法通知所有已注册的观察者。ConcreteObserver 类是实现了 Observer 接口的具体观察者,当接收到 update() 方法调用时,会做出相应的反应。在 Main 类的测试代码中,我们创建了一个具体的被观察者对象,并添加了两个观察者,然后改变被观察者的状态,可以看到观察者都收到了通知并进行了更新。 

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

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

相关文章

【Harmony3.1/4.0】笔记六-对话框

概念 对话框在任何一款应用中&#xff0c;任何一个系统或者平台上使用都非常频繁&#xff0c;这里介绍一下鸿蒙系统中对话框的用法&#xff0c;分别为:普通文本对话框&#xff0c;自定义提示对话框&#xff0c;对话框菜单&#xff0c;警告提示对话框&#xff0c;列表选择对话框…

yolov8源码安装

YOLOv8 是目前最新的 YOLO 模型版本&#xff0c;但是请注意&#xff0c;在撰写本回答时&#xff08;2023年&#xff09;&#xff0c;YOLOv8 可能尚未发布或者还在开发中。因此&#xff0c;以下指导适用于如何安装任何 YOLO 模型的典型源代码。 确保你有正确的 Python 环境。YOL…

Unity 实现原神中的元素反应

一、元素反应 原神中共有七种元素&#xff0c;分别是水、火、冰、岩、风、雷、草。这七种元素能互相作用 Demo下载&#xff1a;Download 元素反应表格图示&#xff0c;可能不够精准 /火水雷冰草岩风绽放原激化火/蒸发超载融化燃烧结晶扩散烈绽放/水蒸发/感电冻结/碎冰绽放结晶…

第7章:网络编程和并发服务器

第7章&#xff1a;网络编程和并发服务器 网络编程是现代软件开发中的一个重要方面&#xff0c;GO语言的并发模型和简洁的语法使得它在网络编程领域表现出色。本章将介绍GO语言的网络编程基础&#xff0c;包括如何使用GO语言创建并发服务器&#xff0c;处理HTTP请求&#xff0c…

Windows主机入侵检测与防御内核技术深入解析

第2章 模块防御的设计思想 2.1 执行与模块执行 本章内容为介绍模块执行防御。在此我将先介绍“执行”分类&#xff0c;以及“模块执行”在“执行”中的位置和重要性。 2.1.1 初次执行 恶意代码&#xff08;或者行为&#xff09;要在被攻击的机器上执行起来&#xff0c;看起…

Ubuntu 自己写的程序如何创建快捷方式

在Ubuntu中创建程序的快捷方式通常是通过将一个指向程序可执行文件的.desktop文件放入/usr/share/applications/或用户的~/.local/share/applications/目录来实现的。以下是创建快捷方式的基本步骤和示例&#xff1a; 在application里创建快捷方式 创建一个新的.desktop文件。…

【Linux】详解信号产生的方式

一、kill命令 在命令行中通过kill -数字 pid指令可以给指定进程发送指定信号。这里说明一下几个常见的信号&#xff1a; SIGINT&#xff08;2号信号&#xff09;&#xff1a;中断信号&#xff0c;通常由用户按下CtrlC产生&#xff0c;用于通知进程终止。SIGQUIT&#xff08;3号…

PG修改端口号与error: could not connect to server: could not connect to server 问题解决

刚开始学习PG修改端口号之后数据库端口号没变。 修改端口号&#xff1a;/usr/local/pgsql/data中的postgresql.conf中 修改后并不能直接生效需要重启PG&#xff1a; /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l /usr/local/pgsql/data/logfile restart重启后新…

【ARM CoreLink 系列 4.3 -- NI-700 Component and interface identifiers】

文章目录 Component and interface identifiers节点ID接口IDCalculation of output IDsID reductionComponent and interface identifiers 在CoreLink NI-700中,每个网络接口和外部接口都有一个独特的标识符,用于确保数据包正确路由。NI-700组件和外部接口使用不同类型的标识…

c++在visual studio上的默认配置

右键 新建项 右键源文件 属性

企业OA管理|基于SprinBoot+vue的企业OA管理系统(源码+数据库+文档)

企业OA管理目录 基于SprinBootvue的企业OA管理系统 一、前言 二、系统设计 三、系统功能设计 1 管理员模块的实现 1.1 用户信息管理 1.2 公告信息管理 1.3 客户关系管理 1.4 通讯录管理 2 用户模块的实现 2.1 客户关系添加 2.2 通讯录添加 2.3 日程安排添加 四、…

3.Docker常用镜像命令和容器命令详解

文章目录 1、Docker镜像命令1.1 获取镜像1.2 查看镜像1.2.1、images命令列出镜像1.2.2、tag命令添加镜像标签1.2.3、inspect命令查看详细信息1.2.4、history命令查看镜像历史 1.3 搜索镜像1.4 删除和清理镜像1.4.1、使用标签删除镜像1.4.2、清理镜像 1.5 创建镜像1.5.1、基于已…

Nginx 从入门到实践(1)

Nginx 从入门到实践 Nginx Nginx 从入门到实践Nginx介绍Nginx常用功能1、Http代理&#xff0c;反向代理2、负载均衡3、动静分离4、Nginx配置文件结构 简述Nginx和Apache的差异编译安装nginx服务在线安装nginxnginx 状态统计nginx 访问控制(用户校验、客户端授权)用户校验基于客…

【vue,unapi】UniApp引入全局js实现全局方法,全局变量

创建一个全局文件utils.js export const baseUrl "https://www.baidu.com/"export const fn () > {console.log("demo"); } export const obj {baseUrl : "https://www.baidu.com/",demo(){console.log("demo2");} }第一种&#…

几个小方法教你如何适应电销工作

什么是电销&#xff1f; 电销&#xff0c;也叫电话销售&#xff0c;是指通过电话等前端工具对客户进行推销的一种销售方式。它以电话沟通为手段&#xff0c;销售员通过电话的方式向客户介绍公司的产品或服务&#xff0c;最终达到促成交易的效果。 不喜欢做电销的原因 不喜欢…

4月25日 C++day4

#include <iostream> using namespace std;class Person {const string name;int age;char sex; public:Person():name("lisi"){cout << "Person无参构造" << endl;}Person(string name,int age,char sex):name(name),age(age),sex(sex)…

电流电压;为什么用铜线作为导体

目录 电流电压 为什么用铜线作为导体 电流:电是怎么流动的 电压:储存压力

最新windows版本erlang26.0和rabbitmq3.13下载

Erlang下载 官网下载&#xff1a;https://www.erlang.org/patches/otp-26.0 百度网盘&#xff1a;https://pan.baidu.com/s/1xU4syn14Bh7QR-skjm_hOg 提取码&#xff1a;az1t RabbtitMQ下载 官网下载&#xff1a;https://www.rabbitmq.com/docs/install-windows 百度网盘…

0054__【Linux】 sed命令详解

【Linux】 sed命令详解_linux sed-CSDN博客

Python二进制文件转换为文本文件

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在日常编程中&#xff0c;我们经常会遇到需要将二进制文件转换为文本文件的情况。这可能是因…