深入理解观察者模式 —— Qt信号槽机制的实现

server/2025/1/18 4:10:19/

观察者模式是一种行为型设计模式,允许一个对象(被观察者)状态发生变化时通知一组依赖它的对象(观察者),从而实现对象之间的解耦。在这篇文章中,我们将探讨如何用 C++Python 实现观察者模式,并在代码中清晰地体现这一设计模式的核心思想。

其实Qt的信号槽机制,就是借住了这一设计模式,并对其进行了一些扩展。由于Qt广泛的被C++和Python用户使用,所以这里给出Python和C++两个版本的简单实现示例。


观察者模式的核心

  1. Subject(被观察者)
    维护观察者列表并提供注册、移除和通知观察者的机制。

  2. Observer(观察者)
    定义接口,提供 update 方法,供被观察者在状态变化时调用。

  3. ConcreteObserver(具体观察者)
    实现观察者接口,响应被观察者的通知。


C++ 实现观察者模式

代码实现
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>// 抽象观察者接口
class Observer {
public:virtual ~Observer() = default;virtual void update(const std::string& message) = 0;
};// 被观察者类
class Subject {
public:void attach(const std::shared_ptr<Observer>& observer) {m_observers.push_back(observer);}void detach(const std::shared_ptr<Observer>& observer) {m_observers.erase(std::remove(m_observers.begin(), m_observers.end(), observer),m_observers.end());}void notify(const std::string& message) {for (const auto& observer : m_observers) {if (observer) {observer->update(message);}}}private:std::vector<std::shared_ptr<Observer>> m_observers; // 观察者列表
};// 具体观察者
class ConcreteObserver : public Observer {
public:explicit ConcreteObserver(const std::string& name) : m_name(name) {}void update(const std::string& message) override {std::cout << "Observer [" << m_name << "] received message: " << message << std::endl;}private:std::string m_name; // 观察者名称
};int main() {// 创建被观察者Subject subject;// 创建具体观察者auto observer1 = std::make_shared<ConcreteObserver>("Observer1");auto observer2 = std::make_shared<ConcreteObserver>("Observer2");// 注册观察者subject.attach(observer1);subject.attach(observer2);// 通知所有观察者std::cout << "Sending notification: 'Event A occurred'" << std::endl;subject.notify("Event A occurred");// 移除一个观察者subject.detach(observer1);// 再次通知std::cout << "Sending notification: 'Event B occurred'" << std::endl;subject.notify("Event B occurred");return 0;
}
输出结果
Sending notification: 'Event A occurred'
Observer [Observer1] received message: Event A occurred
Observer [Observer2] received message: Event A occurred
Sending notification: 'Event B occurred'
Observer [Observer2] received message: Event B occurred

Python 实现观察者模式

代码实现
from typing import List# 抽象观察者接口
class Observer:def update(self, message: str):"""被通知时调用的方法"""raise NotImplementedError("Subclass must implement abstract method")# 被观察者类
class Subject:def __init__(self):self._observers: List[Observer] = []  # 观察者列表def attach(self, observer: Observer):"""注册观察者"""if observer not in self._observers:self._observers.append(observer)def detach(self, observer: Observer):"""移除观察者"""if observer in self._observers:self._observers.remove(observer)def notify(self, message: str):"""通知所有观察者"""for observer in self._observers:observer.update(message)# 具体观察者
class ConcreteObserver(Observer):def __init__(self, name: str):self._name = namedef update(self, message: str):print(f"Observer {self._name} received message: {message}")# 测试代码
if __name__ == "__main__":# 创建被观察者subject = Subject()# 创建具体观察者observer1 = ConcreteObserver("Observer1")observer2 = ConcreteObserver("Observer2")# 注册观察者subject.attach(observer1)subject.attach(observer2)# 通知所有观察者print("Sending notification: 'Event A occurred'")subject.notify("Event A occurred")# 移除一个观察者subject.detach(observer1)# 再次通知print("Sending notification: 'Event B occurred'")subject.notify("Event B occurred")
输出结果
Sending notification: 'Event A occurred'
Observer Observer1 received message: Event A occurred
Observer Observer2 received message: Event A occurred
Sending notification: 'Event B occurred'
Observer Observer2 received message: Event B occurred

比较与分析

  1. C++ 和 Python 的对比

    • C++ 通过智能指针(std::shared_ptr)管理观察者生命周期,防止内存泄漏。
    • Python 利用动态特性和内置容器(如 list),代码更简洁,开发效率更高。
  2. 扩展性

    • 两种语言都可以轻松扩展为支持更复杂的通知机制,例如传递多参数、支持异步事件等。

总结

观察者模式通过将事件通知和响应解耦,为多对象之间的通信提供了一种灵活的解决方案。无论是 C++ 还是 Python,都可以方便地实现这一模式,并根据需求进行扩展。

思考

值得关注的一点,需要读者自己去尝试和实现:

  • 如何在信号槽机制中添加自定义参数

http://www.ppmy.cn/server/159255.html

相关文章

3、C#基于.net framework的应用开发实战编程 - 实现(三、一) - 编程手把手系列文章...

三、 实现&#xff1b; 三&#xff0e;一、实现数据库操作&#xff1b; 对于数据库的操作&#xff0c;以前都是有ODBC的接口&#xff0c;通过Helper类库进行的操作。此文主要介绍例子里对数据库操作的实现。 1、 SQLiteHelper&#xff1b; SQLite主要是用C编写的&#xff0c;但…

YOLOv8从菜鸟到精通(二):YOLOv8数据标注以及模型训练

数据标注 前期准备 先打开Anaconda Navigator&#xff0c;点击Environment&#xff0c;再点击new(new是我下载anaconda的文件夹名称)&#xff0c;然后点击创建 点击绿色按钮&#xff0c;并点击Open Terminal 输入labelimg便可打开它,labelimg是图像标注工具&#xff0c;在上篇…

微软徽标认证WHQL

什么是微软徽标认证&#xff1f; WHQL认证&#xff0c;也叫Windows徽标认证&#xff0c;是Windows硬件设备质量实验室&#xff08;Windows Hardware Quality Labs&#xff09;的简称。由微软设立的这项认证标准&#xff0c;通过使用微软的测试平台对设备和驱动进行严格的测试&a…

【Rust自学】12.5. 重构 Pt.3:移动业务逻辑

12.5.0. 写在正文之前 第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print)&#xff0c;是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。 这个项目分为这么几步&#xff1a; 接收命令行参数读取…

PostMan测试webSocket接口(保姆级教程)

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01; 也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&…

springboot 整合jsp

注意&#xff1a; 非专业技术人员用于研究 该项目已过时 pom.xml依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&quo…

《深入理解Mybatis原理》Mybatis中的缓存实现原理

一级缓存实现 什么是一级缓存&#xff1f; 为什么使用一级缓存&#xff1f; 每当我们使用MyBatis开启一次和数据库的会话&#xff0c;MyBatis会创建出一个SqlSession对象表示一次数据库会话。 在对数据库的一次会话中&#xff0c;我们有可能会反复地执行完全相同的查询语句&…

《AI赋能鸿蒙Next,打造极致沉浸感游戏》

在游戏开发领域&#xff0c;鸿蒙Next系统与人工智能技术的结合为开发者们带来了前所未有的机遇&#xff0c;使打造更具沉浸感的游戏成为可能。以下将深入探讨如何利用人工智能在鸿蒙Next上开发出令人身临其境的游戏。 利用AI优化游戏角色智能行为 在传统游戏中&#xff0c;非…