设计模式-observer模式(观察者模式)

news/2025/2/24 15:30:57

解释

观察者模式用于建立对象间的一对多依赖,当主题(Subject)状态变化时,所有观察者(Observers)自动收到通知。
Observer 模式应该可以说是应用最多、影响最广的模式之一,因为 Observer 的一个实例 Model/View/Control(MVC)结构在系统开发架构设计中有着很重要的地位和意义。

典型应用场景

  1. GUI事件响应(如按钮点击触发多个控件更新)
  2. 数据实时同步(如股票价格变动通知多个客户端)
  3. 游戏引擎中的事件系统(如角色死亡触发任务失败/成就解锁)

不使用观察者模式的缺点

假设要实现一个天气监测系统,当温度变化时通知手机App和LED屏幕。若直接硬编码调用所有依赖对象,会导致以下问题:

  1. 高耦合主题类(WeatherStation)直接依赖具体观察者类,违反依赖倒置原则。
  2. 扩展困难新增观察者类型(如Web界面)需修改主题类代码,违反开闭原则。
  3. 代码冗余需要手动管理所有观察者的调用逻辑。

代码对比

1. 不使用观察者模式(硬编码调用)

#include <iostream>

// 具体显示设备类(紧耦合)
class MobileApp {
public:
    void update(float temp) {
        std::cout << "Mobile App: Temperature updated to " << temp << "°C\n";
    }
};

class LEDScreen {
public:
    void refresh(float temp) {
        std::cout << "LED Screen: New temperature = " << temp << "°C\n";
    }
};

// 主题类(直接管理所有设备)
class WeatherStation {
    MobileApp* mobileApp;  // 直接依赖具体类
    LEDScreen* ledScreen;  // 新增设备需添加成员变量
    float temperature;

public:
    WeatherStation(MobileApp* app, LEDScreen* screen) 
        : mobileApp(app), ledScreen(screen) {}

    void setTemperature(float temp) {
        temperature = temp;
        // 必须显式调用所有设备的方法
        mobileApp->update(temp);
        ledScreen->refresh(temp);
    }
};

int main() {
    MobileApp app;
    LEDScreen screen;
    WeatherStation station(&app, &screen);
    station.setTemperature(25.5);
}

缺点分析:

  • 添加新设备(如WebInterface)需修改WeatherStation的构造函数和setTemperature方法。
  • 主题类与具体观察者类紧密耦合,单元测试困难。
  • 无法动态增减观察者(如运行时移除LED屏幕)。

2. 使用观察者模式优化

#include <iostream>
#include <vector>
#include <algorithm>

// 抽象观察者接口(解耦核心)
class Observer {
public:
    virtual void update(float temp) = 0;
    virtual ~Observer() = default;
};

// 具体观察者实现
class MobileApp : public Observer {
public:
    void update(float temp) override {
        std::cout << "Mobile App: Temperature updated to " << temp << "°C\n";
    }
};

class LEDScreen : public Observer {
public:
    void update(float temp) override {
        std::cout << "LED Screen: New temperature = " << temp << "°C\n";
    }
};

// 主题类(面向接口编程)
class WeatherStation {
    std::vector<Observer*> observers;  // 统一管理抽象接口
    float temperature;

public:
    void addObserver(Observer* obs) { observers.push_back(obs); }
    void removeObserver(Observer* obs) {
        observers.erase(std::remove(observers.begin(), observers.end(), obs), observers.end());
    }

    void setTemperature(float temp) {
        temperature = temp;
        for (auto obs : observers) {
            obs->update(temp);  // 统一调用接口
        }
    }
};

int main() {
    WeatherStation station;
    MobileApp app;
    LEDScreen screen;

    station.addObserver(&app);
    station.addObserver(&screen);
    station.setTemperature(25.5);  // 输出两者的通知

    // 动态移除观察者
    station.removeObserver(&screen);
    station.setTemperature(26.0);  // 仅输出Mobile App
}

优点:

  • 主题类仅依赖抽象接口Observer,与具体实现解耦。
  • 支持动态增删观察者,扩展性极强(新增设备只需实现Observer接口)。
  • 符合开闭原则,无需修改已有代码即可扩展功能。

总结

场景不使用观察者模式使用观察者模式
耦合度高(直接依赖具体类)低(依赖抽象接口)
扩展性差(需修改主题类代码)优(新增观察者无需修改主题类)
动态管理不支持运行时增删观察者支持
代码维护成本高(重复调用逻辑)低(统一管理逻辑)

观察者模式通过抽象接口和动态注册机制,有效解决了硬编码调用带来的耦合性问题,是解耦对象间通知关系的经典方案。


http://www.niftyadmin.cn/n/5864530.html

相关文章

基于 go-wrk 在 Windows 环境下对 Go Web 应用进行 HTTP 压力测试

基于 go-wrk 在 Windows 环境下对 Go Web 应用进行 HTTP 压力测试 这部分内容参考并搬运自 q1mi 老师的技术博客&#xff0c;原文的链接为&#xff1a;https://liwenzhou.com/posts/Go/benchmark-tools/。 压测相关术语 响应时间&#xff08;RT&#xff09;&#xff1a;指系…

React之旅-04 路由详解

React Router 路由库提供了多种路由组件&#xff0c;详解如下&#xff1a; BrowserRouter&#xff1a;为应用程序提供路由环境&#xff0c;示例代码&#xff1a; import { BrowserRouter } from react-router-dom; ReactDOM.createRoot(document.getElementById(root)).rende…

【deepseek之我学】如何理解golang的gmp模型

Go语言的GMP模型是其并发机制的核心&#xff0c;它高效地管理了成千上万的Goroutine。以下是对GMP模型的详细解释&#xff1a; --- ### **1. GMP三个核心组件** - **G (Goroutine)**&#xff1a; - 轻量级用户态协程&#xff0c;初始栈大小仅2KB&#xff08;可动态扩容&…

Keepalive基础

一。简介和功能 vrrp协议的软件实现&#xff0c;原生设计目的是为了高可用ipvs服务 功能&#xff1a; 1.基于vrrp协议完成地址流动 2.为vip地址所在的节点生成ipvs规则&#xff08;在配置文件中预先定义&#xff09; 3.为ipvs集群的各RS做健康状况检测 4.基于脚本调用接口…

C++/JavaScript ⭐算法OJ⭐用两个队列实现栈

题目描述 225. Implement Stack using Queues Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty). Implement the MyStack class: void push(int…

AI助力小微企业技术开发规范化管理 | 杂谈

AI助力小微企业技术开发规范化管理 在小型技术研发企业中&#xff0c;人员配置紧张&#xff0c;往往一名员工需要承担多项职务和任务。例如&#xff0c;后端程序开发人员可能同时要负责需求调研、数据库设计、后端设计及开发&#xff0c;甚至在某些情况下还需兼任架构师的角色。…

Github 2025-02-23 php开源项目日报 Top9

根据Github Trendings的统计,今日(2025-02-23统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目9JavaScript项目2Shell项目1TypeScript项目1Blade项目1Java项目1ASP项目1Vue项目1Laravel:表达力和优雅的 Web 应用程序框架 创建周期:…

彻底卸载kubeadm安装的k8s集群

目录 一、删除资源 二、停止k8s服务 三、重置集群 四、卸载k8s安装包 五、清理残留文件和目录 六、删除k8s相关镜像 七、重启服务器 一、删除资源 # 删除集群中的所有资源&#xff0c;包括 Pod、Deployment、Service&#xff0c;任意节点执行 kubectl delete --all pod…