当前位置: 首页 > news >正文

【设计模式】【行为型模式(Behavioral Patterns)】之观察者模式(Observer Pattern)

1. 设计模式原理说明

观察者模式(Observer Pattern) 是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式非常适合处理事件驱动系统,其中对象之间需要保持同步但又不想紧密耦合。

主要角色
  1. Subject(主题/被观察者):它知道它的观察者是谁。任何数量的观察者都可以观察一个主题。提供注册和删除观察者对象的方法。
  2. Observer(观察者):为那些在主题发生改变时需获得通知的对象定义一个更新接口。
  3. ConcreteSubject(具体主题):将有关状态存储于具体观察者对象;当它的状态发生改变时,向它的各个观察者发出通知。
  4. ConcreteObserver(具体观察者):实现Observer的更新接口以使自身状态与主题的状态保持一致。

2. UML 类图及解释

UML 类图
+------------------+                +-----------------------+
|   Subject        |                |     Observer          |
|------------------|                |-----------------------|
| - observers: List<Observer> |     | - update(message: String): void |
| - register(observer: Observer) |   +-----------------------+
| - unregister(observer: Observer) |   |
| - notifyObservers(message: String) |   |
+------------------+                +-----------------------+||v+---------------------------+| ConcreteObserverA         ||--------------------------|| - update(message: String)  |+---------------------------+^|+---------------------------+| ConcreteObserverB         ||--------------------------|| - update(message: String)  |+---------------------------+
类图解释
  • Subject:定义了添加、删除和通知观察者的接口。具体主题会实现这些方法,并维护一个观察者列表。
  • Observer:定义了一个更新接口,当主题发生变化时,观察者会被调用。
  • ConcreteSubject:实现了Subject接口,当其状态变化时,会通知所有已注册的观察者。
  • ConcreteObserverA 和 ConcreteObserverB:实现了Observer接口,当收到主题的通知时,会更新自己的状态。

3. 代码案例及逻辑详解

Java 代码案例
// 观察者接口
interface Observer {void update(String message);
}// 具体观察者A
class ConcreteObserverA implements Observer {@Overridepublic void update(String message) {System.out.println("ConcreteObserverA received: " + message);}
}// 具体观察者B
class ConcreteObserverB implements Observer {@Overridepublic void update(String message) {System.out.println("ConcreteObserverB received: " + message);}
}// 被观察者接口
interface Subject {void register(Observer observer);void unregister(Observer observer);void notifyObservers(String message);
}// 具体被观察者
class ConcreteSubject implements Subject {private List<Observer> observers = new ArrayList<>();@Overridepublic void register(Observer observer) {observers.add(observer);}@Overridepublic void unregister(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers(String message) {for (Observer observer : observers) {observer.update(message);}}public void changeState() {// 模拟状态变化System.out.println("State changed, notifying observers...");notifyObservers("State has been changed!");}
}// 客户端
public class Client {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();Observer observerA = new ConcreteObserverA();Observer observerB = new ConcreteObserverB();subject.register(observerA);subject.register(observerB);subject.changeState();subject.unregister(observerA);subject.changeState();}
}
C++ 代码案例
#include <iostream>
#include <vector>
#include <algorithm>// 观察者接口
class Observer {
public:virtual void update(const std::string& message) = 0;virtual ~Observer() {}
};// 具体观察者A
class ConcreteObserverA : public Observer {
public:void update(const std::string& message) override {std::cout << "ConcreteObserverA received: " << message << std::endl;}
};// 具体观察者B
class ConcreteObserverB : public Observer {
public:void update(const std::string& message) override {std::cout << "ConcreteObserverB received: " << message << std::endl;}
};// 被观察者接口
class Subject {
private:std::vector<Observer*> observers;
public:void registerObserver(Observer* observer) {observers.push_back(observer);}void unregisterObserver(Observer* observer) {observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notifyObservers(const std::string& message) {for (Observer* observer : observers) {observer->update(message);}}void changeState() {// 模拟状态变化std::cout << "State changed, notifying observers..." << std::endl;notifyObservers("State has been changed!");}
};// 客户端
int main() {Subject subject;Observer* observerA = new ConcreteObserverA();Observer* observerB = new ConcreteObserverB();subject.registerObserver(observerA);subject.registerObserver(observerB);subject.changeState();subject.unregisterObserver(observerA);subject.changeState();delete observerA;delete observerB;return 0;
}
Python 代码案例
# 观察者接口
class Observer:def update(self, message):raise NotImplementedError# 具体观察者A
class ConcreteObserverA(Observer):def update(self, message):print(f"ConcreteObserverA received: {message}")# 具体观察者B
class ConcreteObserverB(Observer):def update(self, message):print(f"ConcreteObserverB received: {message}")# 被观察者接口
class Subject:def __init__(self):self._observers = []def register(self, observer):self._observers.append(observer)def unregister(self, observer):self._observers.remove(observer)def notify_observers(self, message):for observer in self._observers:observer.update(message)def change_state(self):# 模拟状态变化print("State changed, notifying observers...")self.notify_observers("State has been changed!")# 客户端
if __name__ == "__main__":subject = Subject()observerA = ConcreteObserverA()observerB = ConcreteObserverB()subject.register(observerA)subject.register(observerB)subject.change_state()subject.unregister(observerA)subject.change_state()
Go 代码案例
package mainimport ("fmt"
)// 观察者接口
type Observer interface {update(message string)
}// 具体观察者A
type ConcreteObserverA struct{}func (c *ConcreteObserverA) update(message string) {fmt.Printf("ConcreteObserverA received: %s\n", message)
}// 具体观察者B
type ConcreteObserverB struct{}func (c *ConcreteObserverB) update(message string) {fmt.Printf("ConcreteObserverB received: %s\n", message)
}// 被观察者接口
type Subject struct {observers []Observer
}func (s *Subject) register(observer Observer) {s.observers = append(s.observers, observer)
}func (s *Subject) unregister(observer Observer) {for i, obs := range s.observers {if obs == observer {s.observers = append(s.observers[:i], s.observers[i+1:]...)break}}
}func (s *Subject) notifyObservers(message string) {for _, observer := range s.observers {observer.update(message)}
}func (s *Subject) changeState() {// 模拟状态变化fmt.Println("State changed, notifying observers...")s.notifyObservers("State has been changed!")
}// 客户端
func main() {subject := &Subject{}observerA := &ConcreteObserverA{}observerB := &ConcreteObserverB{}subject.register(observerA)subject.register(observerB)subject.changeState()subject.unregister(observerA)subject.changeState()
}

4. 总结

观察者模式 是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式特别适用于事件驱动系统,其中对象之间需要保持同步但又不想紧密耦合。

主要优点
  1. 松耦合:观察者和被观察者之间的依赖关系非常松散,观察者不需要知道被观察者的具体实现细节。
  2. 支持广播通信:被观察者可以向所有注册的观察者广播消息,无需关心观察者的具体类型。
  3. 灵活性高:可以动态地添加或删除观察者,而不会影响其他观察者。
主要缺点
  1. 过度通知:如果被观察者的状态频繁变化,可能会导致大量不必要的通知。
  2. 复杂性增加:引入观察者模式会增加系统的复杂性,特别是当观察者和被观察者之间存在复杂的依赖关系时。
  3. 调试困难:由于观察者模式涉及多个对象之间的交互,调试时可能比较困难。
适用场景
  • 当一个对象的改变需要同时改变其他对象,而且不知道具体有多少对象需要改变时。
  • 当一个对象需要在另一个对象的状态发生变化时自动更新,但又不想紧密耦合时。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面,将这两者封装在独立的对象中以提高可复用性和灵活性时。

相关文章:

【设计模式】【行为型模式(Behavioral Patterns)】之观察者模式(Observer Pattern)

1. 设计模式原理说明 观察者模式&#xff08;Observer Pattern&#xff09; 是一种行为设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都会得到通知并自动更新。这种模式非常适合处理事件驱动系统&a…...

文件导入-使用java反射修改日期数据

文件导入时&#xff0c;时间类型通常不能直接导出&#xff0c;以下方法为批量处理类中日期类型转字符串类型。 Date/Datetime --> String(yyyy-mm-dd)Field[] declaredFields HrAviationstudentMonitorDTO.class.getDeclaredFields(); for (Field field : declaredFields) …...

【网络安全设备系列】10、安全审计系统

0x00 定义: 网络安全审计系统针对互联网行为提供有效的行为审计、内容审计、行为报警、行为控制及相关审计功能。从管理层面提供互联网的 有效监督&#xff0c;预防、制止数据泄密。满足用户对互联网行为审计备案及 安全保护措施的要求&#xff0c;提供完整的上网记录&#xf…...

Apache Maven Assembly 插件简介

Apache Maven Assembly 插件是一个强大的工具&#xff0c;允许您以多种格式&#xff08;如 ZIP、TAR 和 JAR&#xff09;创建项目的分发包。 该插件特别适用于将项目与其依赖项、配置文件和其他必要资源一起打包。 通过使用 Maven Assembly 插件&#xff0c;您可以将项目作为…...

ReentrantLock(可重入锁) Semaphore(信号量) CountDownLatch

目录 ReentrantLock(可重入锁) &Semaphore(信号量)&CountDownLatchReentrantLock(可重入锁)既然有了synchronized&#xff0c;为啥还要有ReentrantLock?Semaphore(信号量)如何确保线程安全呢&#xff1f;CountDownLatch ReentrantLock(可重入锁) &Semaphore(信号量…...

计算机网络习题解答--个人笔记(未完)

本篇文章为关于《计算机网络-自顶向下方法第七版》的阅读总结和课后习题解答(未完待续) 第二章&#xff1a; cookie&#xff1a;&#xff08;这里是比较老版本的HTTP&#xff0c;具体HTTPs是怎么实现的不是很清楚&#xff09;cookie的原理其实很简单。就是在HTTP消息头上又多…...

java虚拟机——频繁发生Full GC的原因有哪些?如何避免发生Full GC

什么是Full GC Full GC&#xff08;Full Garbage Collection&#xff09;是Java垃圾收集过程中的一种形式&#xff0c;它涉及整个堆内存&#xff08;包括年轻代和老年代&#xff09;以及方法区的垃圾收集。Full GC是一个相对重量级的操作&#xff0c;因为它需要遍历和回收整个…...

python学习笔记(12)算法(5)迭代与递归

一、迭代 迭代&#xff08;iteration&#xff09;是一种重复执行某个任务的控制结构。在迭代中&#xff0c;程序会在满足一定的条件下重复执行某段代码&#xff0c;直到这个条件不再满足。 迭代通常用于解决需要逐步推进的计算问题&#xff0c;例如遍历数组、计算阶乘等。迭代…...

从零开始:Linux 环境下的 C/C++ 编译教程

个人主页&#xff1a;chian-ocean 文章专栏 前言&#xff1a; GCC&#xff08;GNU Compiler Collection&#xff09;是一个功能强大的编译器集合&#xff0c;支持多种语言&#xff0c;包括 C 和 C。其中 gcc 用于 C 语言编译&#xff0c;g 专用于 C 编译。 Linux GCC or G的安…...

Rust学习(十):计算机科学简述

Rust学习&#xff08;十&#xff09;&#xff1a;计算机科学简述 在计算机技术这片广袤的领域中&#xff0c;深入理解其内在机制与逻辑需要付出诸多努力。 学习基础知识是构建计算机技术能力大厦的基石&#xff0c;而这一过程往往漫长而艰辛。只有在对基础知识有了扎实的掌握…...

【西瓜书】剪枝与样本值处理——预剪枝、后剪枝、连续值、缺失值

目录 预剪枝 后剪枝 处理连续值 处理缺失值 剪枝&#xff08;pruning&#xff09;是决策树学习算法对付“过拟合”的主要手段。 在决策树学习过程中&#xff0c;有时会造成决策树分枝过多&#xff0c;就可能造成过拟合&#xff0c;可通过主动去掉一些分支来降低过离合的风…...

NLP 1、人工智能与NLP简介

人人都不看好你&#xff0c;可偏偏你最争气 —— 24.11.26 一、AI和NLP的基本介绍 1.人工智能发展流程 弱人工智能 ——> 强人工智能 ——> 超人工智能 ① 弱人工智能 人工智能算法只能在限定领域解决特定的问题 eg&#xff1a;特定场景下的文本分类、垂直领域下的对…...

常见线程安全问题之Double Checked Locking

创作内容丰富的干货文章很费心力&#xff0c;感谢点过此文章的读者&#xff0c;点一个关注鼓励一下作者&#xff0c;激励他分享更多的精彩好文&#xff0c;谢谢大家&#xff01; 双重锁定检查&#xff08;Double Checked Locking&#xff0c;下称 DCL&#xff09;是并发下实现懒…...

Redis(非关系型数据库)的作用 详细解读

edis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、高性能的、基于内存的数据结构存储系统。它具有极高的读写性能&#xff0c;并且能够支持多种数据结构的存储。Redis 最初的设计目标是作为一个缓存解决方案&#xff0c;但随着其功能的不断扩展&#xff0c;…...

互联网视频推拉流EasyDSS视频直播点播平台视频转码有哪些技术特点和应用?

视频转码本质上是一个先解码再编码的过程。在转码过程中&#xff0c;原始视频码流首先被解码成原始图像数据&#xff0c;然后再根据目标编码标准、分辨率、帧率、码率等参数重新进行编码。这样&#xff0c;转换前后的码流可能遵循相同的视频编码标准&#xff0c;也可能不遵循。…...

python之多元线性回归

目录 前言实战 前言 多元线性回归是回归分析中的一种复杂模型&#xff0c;它考虑了多个输入变量对输出变量的影响。与一元线性回归不同&#xff0c;多元线性回归通过引入多个因素&#xff0c;更全面地建模了系统关系。 多元线性回归模型的表达式为&#xff1a; f ( X ) K T …...

学习threejs,使用设置lightMap光照贴图创建阴影效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.MeshLambertMaterial…...

一,SQL注入解题(猫舍)

封神台 第一章&#xff1a;为了女神小芳&#xff01; Tips: 通过sql注入拿到管理员密码&#xff01; 尤里正在追女神小芳&#xff0c;在得知小芳开了一家公司后&#xff0c;尤里通过whois查询发现了小芳公司网站 学过一点黑客技术的他&#xff0c;想在女神面前炫炫技。于是他…...

海康大华宇视视频平台EasyCVR私有化部署视频平台海康ISUP是什么?如何接入到EasyCVR?

在现代安防领域&#xff0c;随着技术的发展和需求的增加&#xff0c;对于视频监控系统的远程管理和互联互通能力提出了更高的要求。海康威视的ISUP协议&#xff08;以及功能相似的EHOME协议&#xff09;因此应运而生&#xff0c;它们为不具备固定IP接入的设备提供了一种有效的中…...

Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 前言&#xff1a;在 Java编程的广袤世界里&#xff0c;数据结构犹如精巧的建筑蓝图&#xff0c;决定着程序在数据处理与存储时的效率、灵活性以…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...