找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4400

积分

0

好友

603

主题
发表于 1 小时前 | 查看: 3| 回复: 0

想象一下,你是一名C++程序员,负责开发公司核心的股票交易系统。系统的核心是一个Stock类,它存储股票的实时价格:

class Stock {
private:
std::string symbol_;
double price_;
public:
void setPrice(double price){ price_ = price; }
double getPrice() const{ return price_; }
};

现在有三个模块需要关注股价变化:交易界面要显示最新价格;价格超过阈值时预警模块要发出警报;每次价格变动日志模块要记录日志。

现在问题来了:当股价变化时,这三个模块怎么知道?

轮询检查:低效且不可靠

第一个想法很直接:让每个模块不停地查询股价。

// 交易界面的代码
void TradingUI::run(){
double lastPrice = 0;
while (true) {
double currentPrice = stock_->getPrice();
if (currentPrice != lastPrice) {
            updateDisplay(currentPrice);
            lastPrice = currentPrice;
        }
        sleep(1);  // 每1毫秒检查一次
    }
}

预警模块和日志模块也写了类似的代码,各自不停地轮询。

运行一下,功能是对的,但CPU占用率直线飙升。三个模块同时高频轮询,系统资源被大量浪费。更糟糕的是,如果股价在两次轮询之间快速变化了两次(比如100→105→102),中间那次105的变化就丢失了。

这种方法既浪费资源,又可能丢失关键事件。 显然,我们需要一种更好的方法:让Stock主动通知关心它的模块,而不是让模块来反复询问。

硬编码回调:紧耦合的陷阱

聪明的你很快想到了改进方案:Stock内部直接调用需要通知的模块。

class Stock {
private:
double price_;
    TradingUI* ui_;
    AlertSystem* alert_;
    Logger* logger_;

public:
void setPrice(double price){
        price_ = price;
// 直接通知各个模块
        ui_->onPriceChanged(price);
        alert_->onPriceChanged(price);
        logger_->onPriceChanged(price);
    }
};

这下不用轮询了!股价一变,三个模块立刻收到通知。测试一下,CPU占用率降到几乎为零,也不会丢失任何变化。

但一周后,问题来了。产品经理说:“我们要加一个新功能——价格图表模块,也需要监听股价变化。”

你只好打开Stock类,硬着头皮加了一行:

void setPrice(double price){
    price_ = price;
    ui_->onPriceChanged(price);
    alert_->onPriceChanged(price);
    logger_->onPriceChanged(price);
    chart_->onPriceChanged(price);  // 新加的
}

又过了一周,产品经理说:“预警模块下线了,不需要通知它了。” 于是,你又得打开Stock类,删掉对应的一行。

你开始感到不对劲:每次增删一个监听者,都要修改Stock这个核心类的代码。Stock现在“知道”了所有监听者的存在,与它们紧密耦合在一起。

问题暴露了:Stock和监听者之间是硬编码的依赖,无法动态增删,违背了设计模式中推崇的开放-封闭原则。

我们需要一种更优的方法:Stock不知道具体有谁在监听,但能通知所有人。

观察者模式:使用列表解耦

你灵机一动:用一个列表来存储所有监听者!

首先,定义一个统一的观察者接口:

class IPriceObserver {
public:
virtual void onPriceChanged(double newPrice)= 0;
virtual ~IPriceObserver() = default;
};

然后,让所有具体的监听者类实现这个接口:

class TradingUI : public IPriceObserver {
public:
void onPriceChanged(double newPrice) override{
        updateDisplay(newPrice);
    }
};

class AlertSystem : public IPriceObserver {
public:
void onPriceChanged(double newPrice) override{
if (newPrice > threshold_) triggerAlert();
    }
};

最后,Stock类只维护一个观察者列表,完全不知道列表里具体是谁:

class Stock {
private:
double price_;
std::vector<IPriceObserver*> observers_;

public:
void addObserver(IPriceObserver* observer){
        observers_.push_back(observer);
    }

void removeObserver(IPriceObserver* observer){
// 从列表中移除
        observers_.erase(
std::remove(observers_.begin(), observers_.end(), observer),
            observers_.end()
        );
    }

void setPrice(double price){
        price_ = price;
// 通知所有观察者
for (auto* observer : observers_) {
            observer->onPriceChanged(price);
        }
    }
};

现在,可以这样动态地管理监听关系:

Stock stock;
TradingUI ui;
AlertSystem alert;
Logger logger;

stock.addObserver(&ui);
stock.addObserver(&alert);
stock.addObserver(&logger);

stock.setPrice(100.5);  // 三个模块同时收到通知!

// 动态下线预警模块
stock.removeObserver(&alert);

stock.setPrice(101.0);  // 只有ui和logger收到通知

完美!现在增删监听者完全不需要修改Stock类的核心代码了,实现了松耦合。这正是经典观察者模式的精髓。

但随着系统规模扩大,你又发现了一个新的麻烦。

多对多监听:接口爆炸问题

系统不只有Stock一个被观察者了。现在有三种数据源:

class Stock {  ... };      // 股价
class Index {  ... };      // 大盘指数
class Exchange {  ... };   // 汇率

按照上面的思路,每种数据源都要定义自己的观察者接口:

class IPriceObserver { virtual void onPriceChanged(double)= 0; };
class IIndexObserver { virtual void onIndexChanged(double)= 0; };
class IExchangeObserver { virtual void onExchangeChanged(double)= 0; };

AlertSystem预警模块需要监听所有这三种数据——它不得不实现三个接口:

class AlertSystem : public IPriceObserver,
public IIndexObserver,
public IExchangeObserver {
void onPriceChanged(double price) override{ ... }
void onIndexChanged(double index) override{ ... }
void onExchangeChanged(double rate) override{ ... }
};

注册监听时,AlertSystem还得主动知道并关联每一个被观察者对象:

stock.addObserver(&alert);
index.addObserver(&alert);
exchange.addObserver(&alert);

新的问题又暴露了:

  1. 每新增一种数据源,就要新增一个接口,监听者类就要多实现一个接口,导致“接口爆炸”。
  2. 监听者必须知道所有被观察者的存在,并主动注册,耦合度依然不低。
  3. 如果TradingUI也要监听这三种数据,同样的多重继承代码得再写一遍,重复劳动。

有没有办法统一所有的事件,让观察者不需要知道被观察者的具体类型,实现更彻底的解耦?

发布-订阅模式:引入事件总线的终极解耦

一个更巧妙的办法是:引入一个中间人(事件总线),让发布者和订阅者都只和中间人打交道,彼此完全不知情。

class EventBus {
private:
// 事件名 → 回调函数列表
std::map<std::string, std::vector<std::function<void(double)>>> subscribers_;

public:
// 订阅事件
void subscribe(const std::string& event, std::function<void(double)> callback){
        subscribers_[event].push_back(callback);
    }

// 发布事件
void publish(const std::string& event, double data){
if (subscribers_.find(event) != subscribers_.end()) {
for (auto& callback : subscribers_[event]) {
                callback(data);
            }
        }
    }
};

// 全局事件总线
EventBus g_eventBus;

现在,作为发布者的Stock类,完全不知道谁在监听,它只负责发布事件:

class Stock {
private:
double price_;
public:
void setPrice(double price){
        price_ = price;
        g_eventBus.publish("stock.price.changed", price);  // 只管发布
    }
};

而作为订阅者的各个模块,也不需要知道Stock的存在,甚至不需要实现任何特定接口。它们只需在初始化时向事件总线订阅感兴趣的事件:

class TradingUI {
public:
    TradingUI() {
// 在构造函数里订阅事件
        g_eventBus.subscribe("stock.price.changed", [this](double price) {
            updateDisplay(price);
        });
    }
};

class AlertSystem {
public:
    AlertSystem() {
        g_eventBus.subscribe("stock.price.changed", [this](double price) {
if (price > 100) {
std::cout << "Alert! Price too high!" << std::endl;
            }
        });
    }
};

如果需要监听新的事件,比如大盘指数变化,AlertSystem只需新增一行订阅代码,完全不用修改类结构:

AlertSystem() {
    g_eventBus.subscribe("stock.price.changed", [this](double price){...});
    // 轻松扩展,监听新事件
    g_eventBus.subscribe("index.updated", [this](double index){...});
}

这种架构带来了巨大的灵活性:

  • 彻底解耦:发布者和订阅者互不知晓。
  • 易于扩展:新增事件类型或订阅者,无需修改现有核心类。
  • 支持多对多:一个事件可以被多个模块订阅,一个模块也可以订阅多个事件。

C++这类系统编程中,理解并灵活运用观察者模式及其变体发布-订阅模式,是构建高内聚、低耦合复杂系统的关键技能。从最原始的轮询,到硬编码回调,再到标准的观察者列表,最后到完全解耦的事件总线,每一次演进都是为了更好地管理模块间的依赖与通信。

如果你在实现这类模式时遇到关于内存管理、线程安全或性能优化等更深层次的问题,欢迎到云栈社区与更多开发者交流探讨,共同解决实际工程中的挑战。




上一篇:45岁程序员面试直言:年轻人能跑,我知道路,团队需要的是解决问题的人
下一篇:开源AI助手AiPy实测体验:本地自动化工具如何告别“养虾”难题
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-27 05:57 , Processed in 0.665841 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表