C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++观察者模式与策略模式

C++中观察者模式与策略模式的实战指南

作者:星河耀银海

本章介绍了观察者模式和策略模式在C++中的应用,包括它们的核心设计思想、角色划分、实现细节及适用场景,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

18.1 本章学习目标与重点

💡 掌握观察者模式的核心设计思想、角色划分及 C++ 实现细节(同步/异步、线程安全)
💡 理解策略模式的设计原则、适用场景及与简单工厂模式的区别
💡 能够结合实际业务场景(如消息通知、算法切换)灵活运用两种模式
💡 解决模式应用中的关键问题(如观察者解注册、策略动态切换、循环依赖)
重点:观察者模式的事件分发机制、策略模式的算法封装逻辑、两种模式的组合使用技巧

18.2 设计模式进阶认知:行为型模式的核心价值

在 C++ 开发中,行为型设计模式专注于解决“对象之间的交互方式”和“职责分配”问题,相比创建型模式(如工厂、单例)关注“对象创建”,行为型模式更侧重“对象协作”。

18.2.1 为什么需要行为型设计模式?

实际开发中,我们常遇到以下交互相关的问题:

行为型模式通过标准化的交互规则,将“交互逻辑”与“业务逻辑”分离,例如观察者模式定义“通知-响应”规则,策略模式定义“算法封装-动态切换”规则,从而解决上述问题。

18.2.2 行为型模式的核心设计原则

行为型模式的设计始终围绕以下原则展开,这也是本章两种模式的核心指导思想:

  1. 封装变化:将易变的交互逻辑(如通知规则、算法实现)封装为独立的类,避免影响其他代码;
  2. 松耦合协作:交互的双方(如观察者与被观察者、策略使用者与策略实现)仅依赖抽象接口,不依赖具体实现;
  3. 职责单一:每个类只负责一项核心职责(如观察者只负责响应事件,策略类只负责实现算法);
  4. 开闭原则:新增交互逻辑(如新增观察者、新增策略)时,无需修改现有核心代码。

18.3 观察者模式:事件驱动的通知机制

观察者模式(Observer Pattern)又称发布-订阅模式(Publish-Subscribe Pattern),其核心思想是“定义对象间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会收到通知并自动更新”。

18.3.1 核心角色与交互流程

1. 核心角色

2. 交互流程

① 观察者通过被观察者的 RegisterObserver() 方法注册到被观察者中;
② 具体被观察者的状态发生变化;
③ 具体被观察者调用 NotifyObservers() 方法,遍历所有注册的观察者;
④ 被观察者调用每个观察者的 Update() 方法,传递状态变化信息;
⑤ 观察者通过 Update() 方法接收信息并执行相应逻辑。

18.3.2 适用场景

18.3.3 基础实现:同步观察者模式(C++ 示例)

以“气象站数据监控系统”为例:气象站(被观察者)收集温度、湿度数据,当数据更新时,需通知显示屏、手机 App、报警器(观察者)进行相应展示或报警。

步骤 1:定义观察者抽象接口

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 观察者抽象接口
class Observer {
public:
    // 纯虚函数:接收通知并更新(参数:温度、湿度)
    virtual void Update(float temperature, float humidity) = 0;
    // 虚析构函数:确保子类析构时正确释放资源
    virtual ~Observer() {}
};

步骤 2:定义被观察者抽象接口

// 被观察者抽象接口
class Subject {
public:
    // 注册观察者
    virtual void RegisterObserver(Observer* observer) = 0;
    // 解注册观察者
    virtual void RemoveObserver(Observer* observer) = 0;
    // 通知所有观察者
    virtual void NotifyObservers() = 0;
    virtual ~Subject() {}
};

步骤 3:实现具体被观察者(气象站)

// 具体被观察者:气象站
class WeatherStation : public Subject {
private:
    vector<Observer*> observers;  // 存储注册的观察者
    float temperature;            // 温度
    float humidity;               // 湿度
    // 检查观察者是否已注册(辅助函数)
    bool IsObserverRegistered(Observer* observer) const {
        return find(observers.begin(), observers.end(), observer) != observers.end();
    }
public:
    // 注册观察者:添加到列表(避免重复注册)
    void RegisterObserver(Observer* observer) override {
        if (observer == nullptr) {
            throw invalid_argument("观察者不能为空指针!");
        }
        if (!IsObserverRegistered(observer)) {
            observers.push_back(observer);
            cout << "观察者注册成功:" << typeid(*observer).name() << endl;
        } else {
            cout << "观察者已注册,无需重复添加:" << typeid(*observer).name() << endl;
        }
    }
    // 解注册观察者:从列表中移除
    void RemoveObserver(Observer* observer) override {
        auto iter = find(observers.begin(), observers.end(), observer);
        if (iter != observers.end()) {
            observers.erase(iter);
            cout << "观察者解注册成功:" << typeid(*observer).name() << endl;
        } else {
            cout << "观察者未注册,无法解注册:" << typeid(*observer).name() << endl;
        }
    }
    // 通知所有观察者:遍历列表并调用 Update 方法
    void NotifyObservers() override {
        cout << "\n气象站数据更新,开始通知所有观察者..." << endl;
        for (Observer* observer : observers) {
            observer->Update(temperature, humidity);
        }
    }
    // 模拟气象数据更新(外部调用,触发通知)
    void SetWeatherData(float temp, float humi) {
        this->temperature = temp;
        this->humidity = humi;
        cout << "\n气象站数据更新:温度=" << temp << "℃,湿度=" << humi << "%" << endl;
        NotifyObservers();  // 数据更新后自动通知
    }
};

步骤 4:实现具体观察者(显示屏、手机 App、报警器)

// 具体观察者1:显示屏(展示实时数据)
class DisplayScreen : public Observer {
private:
    string screenName;  // 显示屏名称(如"客厅显示屏")
public:
    DisplayScreen(const string& name) : screenName(name) {}
    void Update(float temperature, float humidity) override {
        cout << "[" << screenName << "] 实时数据展示:温度=" << temperature << "℃,湿度=" << humidity << "%" << endl;
    }
};
// 具体观察者2:手机 App(推送通知)
class MobileApp : public Observer {
private:
    string userName;  // 用户名
public:
    MobileApp(const string& name) : userName(name) {}
    void Update(float temperature, float humidity) override {
        cout << "[" << userName << "的手机 App] 推送通知:当前温度" << temperature << "℃,湿度" << humidity << "%" << endl;
    }
};
// 具体观察者3:报警器(湿度超标时报警)
class Alarm : public Observer {
private:
    float humidityThreshold;  // 湿度阈值(超过则报警)
public:
    Alarm(float threshold) : humidityThreshold(threshold) {}
    void Update(float temperature, float humidity) override {
        if (humidity > humidityThreshold) {
            cout << "[报警器] 警告!湿度超标(当前" << humidity << "%,阈值" << humidityThreshold << "%),请及时通风!" << endl;
        } else {
            cout << "[报警器] 湿度正常(当前" << humidity << "%),无报警" << endl;
        }
    }
};

步骤 5:客户端使用示例

int main() {
    try {
        // 1. 创建被观察者:气象站
        WeatherStation weatherStation;
        // 2. 创建观察者
        Observer* livingRoomScreen = new DisplayScreen("客厅显示屏");
        Observer* bedroomScreen = new DisplayScreen("卧室显示屏");
        Observer* userApp = new MobileApp("张三");
        Observer* humidityAlarm = new Alarm(60.0f);  // 湿度阈值60%
        // 3. 注册观察者
        weatherStation.RegisterObserver(livingRoomScreen);
        weatherStation.RegisterObserver(bedroomScreen);
        weatherStation.RegisterObserver(userApp);
        weatherStation.RegisterObserver(humidityAlarm);
        // 4. 模拟数据更新(触发第一次通知)
        weatherStation.SetWeatherData(25.5f, 55.0f);
        // 5. 解注册卧室显示屏(不再接收通知)
        weatherStation.RemoveObserver(bedroomScreen);
        cout << "\n--- 解注册卧室显示屏后 ---" << endl;
        // 6. 模拟数据更新(触发第二次通知,卧室显示屏不再接收)
        weatherStation.SetWeatherData(26.8f, 62.0f);  // 湿度超标,报警器会报警
        // 7. 释放资源
        delete livingRoomScreen;
        delete bedroomScreen;
        delete userApp;
        delete humidityAlarm;
    } catch (const exception& e) {
        cout << "错误:" << e.what() << endl;
    }
    return 0;
}

运行结果

观察者注册成功:class DisplayScreen
观察者注册成功:class DisplayScreen
观察者注册成功:class MobileApp
观察者注册成功:class Alarm

气象站数据更新:温度=25.5℃,湿度=55.0%

气象站数据更新,开始通知所有观察者...
[客厅显示屏] 实时数据展示:温度=25.5℃,湿度=55.0%
[卧室显示屏] 实时数据展示:温度=25.5℃,湿度=55.0%
[张三的手机 App] 推送通知:当前温度25.5℃,湿度55.0%
[报警器] 湿度正常(当前55.0%),无报警

观察者解注册成功:class DisplayScreen

--- 解注册卧室显示屏后 ---

气象站数据更新:温度=26.8℃,湿度=62.0%

气象站数据更新,开始通知所有观察者...
[客厅显示屏] 实时数据展示:温度=26.8℃,湿度=62.0%
[张三的手机 App] 推送通知:当前温度26.8℃,湿度62.0%
[报警器] 警告!湿度超标(当前62.0%,阈值60.0%),请及时通风!

18.3.4 进阶优化:线程安全与异步通知

基础实现是同步通知(被观察者在主线程中依次调用观察者的 Update() 方法),存在两个问题:

  1. 线程安全风险:若多个线程同时修改被观察者状态或注册/解注册观察者,可能导致列表遍历异常;
  2. 性能瓶颈:若某个观察者的 Update() 方法执行耗时较长,会阻塞其他观察者的通知。

优化方案 1:线程安全的同步通知(加锁保护)

使用 std::mutex 保护观察者列表的读写操作,确保多线程环境下的安全性:

#include <mutex>
// 线程安全的气象站(继承自原 WeatherStation,重写核心方法)
class ThreadSafeWeatherStation : public Subject {
private:
    vector<Observer*> observers;
    float temperature;
    float humidity;
    mutex mtx;  // 互斥锁,保护观察者列表
public:
    void RegisterObserver(Observer* observer) override {
        if (observer == nullptr) {
            throw invalid_argument("观察者不能为空指针!");
        }
        lock_guard<mutex> lock(mtx);  // 自动加锁/解锁
        auto iter = find(observers.begin(), observers.end(), observer);
        if (iter == observers.end()) {
            observers.push_back(observer);
            cout << "线程安全注册:" << typeid(*observer).name() << endl;
        }
    }
    void RemoveObserver(Observer* observer) override {
        lock_guard<mutex> lock(mtx);
        auto iter = find(observers.begin(), observers.end(), observer);
        if (iter != observers.end()) {
            observers.erase(iter);
            cout << "线程安全解注册:" << typeid(*observer).name() << endl;
        }
    }
    void NotifyObservers() override {
        lock_guard<mutex> lock(mtx);
        cout << "\n线程安全通知所有观察者..." << endl;
        // 拷贝一份观察者列表,避免遍历过程中列表被修改(如解注册)
        vector<Observer*> tempObservers = observers;
        lock.unlock();  // 提前解锁,减少阻塞时间
        for (Observer* observer : tempObservers) {
            observer->Update(temperature, humidity);
        }
    }
    void SetWeatherData(float temp, float humi) {
        lock_guard<mutex> lock(mtx);
        this->temperature = temp;
        this->humidity = humi;
        cout << "\n线程安全气象站数据:温度=" << temp << "℃,湿度=" << humi << "%" << endl;
        lock.unlock();
        NotifyObservers();  // 通知操作在解锁后执行,避免阻塞写操作
    }
};

优化方案 2:异步通知(使用线程池)

通过线程池异步执行观察者的 Update() 方法,避免单个观察者阻塞整个通知流程:

#include <thread>
#include <queue>
#include <condition_variable>
// 简单线程池(用于异步执行任务)
class ThreadPool {
private:
    vector<thread> workers;       // 工作线程
    queue<function<void()>> tasks;// 任务队列
    mutex mtx;                    // 保护任务队列
    condition_variable cv;        // 条件变量,唤醒线程
    bool stop;                    // 线程池停止标志
public:
    // 构造函数:创建n个工作线程
    ThreadPool(size_t n) : stop(false) {
        for (size_t i = 0; i < n; ++i) {
            workers.emplace_back([this]() {
                while (true) {
                    function<void()> task;
                    {
                        unique_lock<mutex> lock(this->mtx);
                        // 等待任务或停止信号
                        this->cv.wait(lock, [this]() {
                            return this->stop || !this->tasks.empty();
                        });
                        // 线程池停止且任务队列为空,退出线程
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        // 取出任务
                        task = move(this->tasks.front());
                        this->tasks.pop();
                    }
                    // 执行任务
                    task();
                }
            });
        }
    }
    // 析构函数:停止线程池
    ~ThreadPool() {
        {
            unique_lock<mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();  // 唤醒所有工作线程
        for (thread& worker : workers) {
            worker.join();  // 等待所有线程完成
        }
    }
    // 添加任务到线程池
    template<typename F>
    void Enqueue(F&& f) {
        {
            unique_lock<mutex> lock(mtx);
            if (stop) {
                throw runtime_error("线程池已停止,无法添加任务!");
            }
            tasks.emplace(forward<F>(f));
        }
        cv.notify_one();  // 唤醒一个工作线程
    }
};
// 异步通知的气象站
class AsyncWeatherStation : public Subject {
private:
    vector<Observer*> observers;
    float temperature;
    float humidity;
    mutex mtx;
    ThreadPool pool;  // 线程池(2个工作线程)
public:
    AsyncWeatherStation() : pool(2) {}
    void RegisterObserver(Observer* observer) override {
        lock_guard<mutex> lock(mtx);
        if (observer && find(observers.begin(), observers.end(), observer) == observers.end()) {
            observers.push_back(observer);
        }
    }
    void RemoveObserver(Observer* observer) override {
        lock_guard<mutex> lock(mtx);
        auto iter = find(observers.begin(), observers.end(), observer);
        if (iter != observers.end()) {
            observers.erase(iter);
        }
    }
    void NotifyObservers() override {
        lock_guard<mutex> lock(mtx);
        vector<Observer*> tempObservers = observers;
        float temp = temperature;
        float humi = humidity;
        // 异步执行每个观察者的 Update 方法
        for (Observer* observer : tempObservers) {
            pool.Enqueue([observer, temp, humi]() {
                observer->Update(temp, humi);
            });
        }
    }
    void SetWeatherData(float temp, float humi) {
        lock_guard<mutex> lock(mtx);
        this->temperature = temp;
        this->humidity = humi;
        cout << "\n异步气象站数据更新:温度=" << temp << "℃,湿度=" << humi << "%" << endl;
        lock.unlock();
        NotifyObservers();
    }
};

异步通知测试代码

// 模拟耗时的观察者(如网络请求)
class SlowObserver : public Observer {
public:
    void Update(float temperature, float humidity) override {
        cout << "[耗时观察者] 开始处理数据(模拟网络请求)..." << endl;
        this_thread::sleep_for(chrono::seconds(2));  // 模拟2秒耗时
        cout << "[耗时观察者] 数据处理完成:温度=" << temperature << "℃" << endl;
    }
};
int main() {
    AsyncWeatherStation weatherStation;
    Observer* fastObserver = new DisplayScreen("快速显示屏");
    Observer* slowObserver = new SlowObserver();
    weatherStation.RegisterObserver(fastObserver);
    weatherStation.RegisterObserver(slowObserver);
    weatherStation.SetWeatherData(24.0f, 58.0f);
    cout << "主线程继续执行其他任务..." << endl;
    // 等待异步任务完成(实际项目中无需手动等待,线程池会后台处理)
    this_thread::sleep_for(chrono::seconds(3));
    delete fastObserver;
    delete slowObserver;
    return 0;
}

异步通知运行结果

异步气象站数据更新:温度=24.0℃,湿度=58.0%
主线程继续执行其他任务...
[快速显示屏] 实时数据展示:温度=24.0℃,湿度=58.0%
[耗时观察者] 开始处理数据(模拟网络请求)...
[耗时观察者] 数据处理完成:温度=24.0℃

18.3.5 观察者模式的优缺点与避坑指南

优点

✅ 解耦被观察者与观察者:两者仅依赖抽象接口,无需知道对方的具体实现;
✅ 动态扩展:可随时添加/移除观察者,无需修改被观察者代码,符合“开放-封闭原则”;
✅ 广播通知:被观察者状态变化时,自动通知所有注册的观察者,无需手动调用。

缺点

⚠️ 通知顺序不确定:同步通知时,观察者的更新顺序与注册顺序一致,但异步通知时顺序不可控;
⚠️ 循环依赖风险:若观察者与被观察者相互引用,可能导致内存泄漏(需使用弱指针 weak_ptr 解决);
⚠️ 性能开销:若观察者数量过多或 Update() 方法耗时,同步通知会导致性能瓶颈(需异步优化)。

避坑指南

💡 避免观察者列表遍历期间修改列表:注册/解注册时需加锁,或拷贝列表后遍历(如线程安全实现);
💡 处理观察者空指针:注册时校验观察者指针非空,避免通知时崩溃;
💡 内存管理:被观察者不负责销毁观察者,需由客户端统一管理,或使用智能指针(shared_ptr);
💡 避免过度通知:仅在状态真正变化时触发通知,避免无效更新(如气象站数据未变化时不通知)。

18.4 策略模式:算法的封装与动态切换

策略模式(Strategy Pattern)的核心思想是“定义一系列算法,将每个算法封装起来,并且使它们可以相互替换,让算法的变化独立于使用算法的客户”。

18.4.1 核心角色与设计逻辑

1. 核心角色

2. 设计逻辑

将易变的算法(如排序算法、支付方式、压缩算法)封装为独立的策略类,上下文通过依赖注入或动态设置的方式使用策略,从而实现“算法切换无需修改上下文代码”。

18.4.2 适用场景

18.4.3 基础实现:电商折扣策略系统(C++ 示例)

以“电商平台折扣系统”为例:不同用户(普通用户、VIP用户、超级VIP用户)对应不同折扣策略,且平台可能新增“节日折扣”“优惠券折扣”等策略,使用策略模式可灵活扩展。

步骤 1:定义策略抽象接口(折扣算法)

#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 策略抽象:折扣策略
class DiscountStrategy {
public:
    // 纯虚函数:计算折扣后的价格(参数:原价)
    virtual float CalculateDiscount(float originalPrice) const = 0;
    // 获取策略名称(用于日志输出)
    virtual string GetStrategyName() const = 0;
    virtual ~DiscountStrategy() {}
};

步骤 2:实现具体策略(不同折扣算法)

// 具体策略1:普通用户策略(无折扣)
class NormalUserStrategy : public DiscountStrategy {
public:
    float CalculateDiscount(float originalPrice) const override {
        return originalPrice;  // 无折扣,原价返回
    }
    string GetStrategyName() const override {
        return "普通用户无折扣";
    }
};
// 具体策略2:VIP用户策略(9折)
class VIPUserStrategy : public DiscountStrategy {
public:
    float CalculateDiscount(float originalPrice) const override {
        return originalPrice * 0.9f;  // 9折
    }
    string GetStrategyName() const override {
        return "VIP用户9折";
    }
};
// 具体策略3:超级VIP用户策略(8折+满1000减200)
class SuperVIPUserStrategy : public DiscountStrategy {
public:
    float CalculateDiscount(float originalPrice) const override {
        float discountPrice = originalPrice * 0.8f;  // 先打8折
        if (discountPrice >= 1000) {
            discountPrice -= 200;  // 满1000减200
        }
        return discountPrice;
    }
    string GetStrategyName() const override {
        return "超级VIP用户8折+满1000减200";
    }
};
// 具体策略4:节日折扣策略(7折,限节日使用)
class FestivalDiscountStrategy : public DiscountStrategy {
public:
    float CalculateDiscount(float originalPrice) const override {
        return originalPrice * 0.7f;  // 7折
    }
    string GetStrategyName() const override {
        return "节日折扣7折";
    }
};

步骤 3:实现上下文(订单结算系统)

// 上下文:订单结算系统
class OrderSettlement {
private:
    // 持有策略对象(使用智能指针,自动管理内存)
    shared_ptr<DiscountStrategy> strategy;
public:
    // 构造函数:初始化策略(默认普通用户策略)
    OrderSettlement() : strategy(make_shared<NormalUserStrategy>()) {}
    // 动态切换策略(核心方法:支持运行时更换策略)
    void SetDiscountStrategy(shared_ptr<DiscountStrategy> newStrategy) {
        if (newStrategy == nullptr) {
            throw invalid_argument("策略不能为空!");
        }
        strategy = newStrategy;
        cout << "折扣策略切换为:" << strategy->GetStrategyName() << endl;
    }
    // 结算:调用当前策略计算最终价格
    float Settle(float originalPrice) const {
        if (originalPrice < 0) {
            throw invalid_argument("商品原价不能为负数!");
        }
        float finalPrice = strategy->CalculateDiscount(originalPrice);
        cout << "当前策略:" << strategy->GetStrategyName() << endl;
        cout << "商品原价:" << originalPrice << "元,最终价格:" << finalPrice << "元" << endl;
        return finalPrice;
    }
};

步骤 4:客户端使用示例

int main() {
    try {
        // 1. 创建上下文:订单结算系统
        OrderSettlement settlement;
        // 2. 普通用户结算(默认策略)
        cout << "=== 普通用户购买商品(原价500元)===" << endl;
        settlement.Settle(500.0f);
        // 3. 切换为VIP用户策略
        cout << "\n=== VIP用户购买商品(原价800元)===" << endl;
        settlement.SetDiscountStrategy(make_shared<VIPUserStrategy>());
        settlement.Settle(800.0f);
        // 4. 切换为超级VIP用户策略
        cout << "\n=== 超级VIP用户购买商品(原价1500元)===" << endl;
        settlement.SetDiscountStrategy(make_shared<SuperVIPUserStrategy>());
        settlement.Settle(1500.0f);
        // 5. 切换为节日折扣策略
        cout << "\n=== 节日期间购买商品(原价2000元)===" << endl;
        settlement.SetDiscountStrategy(make_shared<FestivalDiscountStrategy>());
        settlement.Settle(2000.0f);
        // 6. 新增策略:无需修改结算系统代码,直接使用
        cout << "\n=== 新增优惠券策略(假设已实现)===" << endl;
        // 假设新增 CouponDiscountStrategy,直接通过 SetDiscountStrategy 切换
        // settlement.SetDiscountStrategy(make_shared<CouponDiscountStrategy>());
        // settlement.Settle(1000.0f);
    } catch (const exception& e) {
        cout << "结算失败:" << e.what() << endl;
    }
    return 0;
}

运行结果

=== 普通用户购买商品(原价500元)===
当前策略:普通用户无折扣
商品原价:500元,最终价格:500元

=== VIP用户购买商品(原价800元)===
折扣策略切换为:VIP用户9折
当前策略:VIP用户9折
商品原价:800元,最终价格:720元

=== 超级VIP用户购买商品(原价1500元)===
折扣策略切换为:超级VIP用户8折+满1000减200
当前策略:超级VIP用户8折+满1000减200
商品原价:1500元,最终价格:1000元

=== 节日期间购买商品(原价2000元)===
折扣策略切换为:节日折扣7折
当前策略:节日折扣7折
商品原价:2000元,最终价格:1400元

=== 新增优惠券策略(假设已实现)===

18.4.4 策略模式与简单工厂模式的区别

很多开发者会混淆策略模式与简单工厂模式,两者的核心区别如下:

对比维度策略模式简单工厂模式
核心目的封装算法,支持动态切换封装对象创建,统一创建逻辑
关注点算法的“使用”与“切换”对象的“创建”
交互方式上下文持有策略对象,主动调用策略方法客户端调用工厂创建对象,自行使用
扩展性新增算法只需新增策略类,无侵入新增产品需修改工厂类(违反开闭原则)
适用场景算法多变、需动态切换产品类型少、创建逻辑简单

💡 实战技巧:策略模式可与简单工厂模式结合使用!例如,在上下文内部通过简单工厂根据条件自动选择策略,无需客户端手动切换:

// 结合简单工厂的上下文
class FactoryStrategySettlement {
private:
    shared_ptr<DiscountStrategy> strategy;
    // 简单工厂:根据用户类型创建策略
    shared_ptr<DiscountStrategy> CreateStrategy(const string& userType) {
        if (userType == "normal") {
            return make_shared<NormalUserStrategy>();
        } else if (userType == "vip") {
            return make_shared<VIPUserStrategy>();
        } else if (userType == "super_vip") {
            return make_shared<SuperVIPUserStrategy>();
        } else {
            throw invalid_argument("不支持的用户类型:" + userType);
        }
    }
public:
    // 根据用户类型自动选择策略
    FactoryStrategySettlement(const string& userType) {
        strategy = CreateStrategy(userType);
    }
    float Settle(float originalPrice) const {
        float finalPrice = strategy->CalculateDiscount(originalPrice);
        cout << "用户类型对应策略:" << strategy->GetStrategyName() << endl;
        cout << "原价:" << originalPrice << "元,最终价:" << finalPrice << "元" << endl;
        return finalPrice;
    }
};
// 客户端使用
int main() {
    FactoryStrategySettlement vipSettlement("vip");
    vipSettlement.Settle(1000.0f);  // 自动使用VIP策略
    return 0;
}

18.4.5 策略模式的优缺点与避坑指南

优点

✅ 算法封装清晰:每个策略类只负责一种算法,职责单一,便于维护;
✅ 支持动态切换:上下文可在运行时切换策略,无需重启程序;
✅ 符合开闭原则:新增算法只需新增策略类,无需修改上下文或其他策略代码;
✅ 避免多重条件判断:替代 if-else/switch,代码更简洁、易扩展。

缺点

⚠️ 类数量增加:每新增一种算法,需新增一个策略类,若算法过多会导致类数量爆炸;
⚠️ 客户端需了解策略:客户端需知道所有策略的存在,才能选择合适的策略(可通过工厂模式优化);
⚠️ 策略间无依赖:若策略之间需要共享数据,需通过上下文传递,增加复杂度。

避坑指南

💡 策略粒度适中:避免将过于简单的算法封装为策略(如仅一行代码的计算),导致过度设计;
💡 使用智能指针管理策略:避免策略对象内存泄漏,简化客户端内存管理;
💡 策略不可变:设计策略类时,尽量使其为无状态类(不存储动态数据),确保线程安全;
💡 复杂策略组合:若需组合多种策略(如“折扣+优惠券”),可结合装饰者模式(后续章节讲解)。

18.5 实战案例:结合观察者模式与策略模式开发智能物流调度系统

18.5.1 需求分析

开发一个智能物流调度系统,核心需求如下:

  1. 订单状态变化时(如“已支付”“已发货”“已签收”),需通知客户 App、商家后台、物流系统;
  2. 调度算法可动态切换:根据订单类型(普通订单、加急订单、大件订单)选择不同的调度策略(如普通调度、优先调度、大件专用调度);
  3. 支持新增订单状态通知对象(如未来新增“保险公司”通知);
  4. 支持新增调度策略(如未来新增“冷链物流调度”);
  5. 多线程环境下安全运行(如同时处理多个订单状态更新)。

18.5.2 设计思路

18.5.3 完整实现代码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <mutex>
#include <memory>
#include <thread>
using namespace std;
// -------------------------- 部分1:观察者模式(订单状态通知) --------------------------
// 观察者抽象:接收订单状态通知
class OrderStatusObserver {
public:
    virtual void OnStatusChanged(const string& orderId, const string& status) = 0;
    virtual string GetObserverName() const = 0;
    virtual ~OrderStatusObserver() {}
};
// 具体观察者1:客户 App
class CustomerAppObserver : public OrderStatusObserver {
public:
    void OnStatusChanged(const string& orderId, const string& status) override {
        cout << "[客户 App] 订单" << orderId << "状态更新为:" << status << ",已推送通知给客户" << endl;
    }
    string GetObserverName() const override {
        return "客户 App";
    }
};
// 具体观察者2:商家后台
class MerchantBackendObserver : public OrderStatusObserver {
public:
    void OnStatusChanged(const string& orderId, const string& status) override {
        cout << "[商家后台] 订单" << orderId << "状态更新为:" << status << ",已同步至商家管理系统" << endl;
    }
    string GetObserverName() const override {
        return "商家后台";
    }
};
// 具体观察者3:物流系统
class LogisticsSystemObserver : public OrderStatusObserver {
public:
    void OnStatusChanged(const string& orderId, const string& status) override {
        cout << "[物流系统] 订单" << orderId << "状态更新为:" << status << ",已触发物流调度" << endl;
    }
    string GetObserverName() const override {
        return "物流系统";
    }
};
// 被观察者:订单
class Order : public Subject {
private:
    string orderId;
    string status;  // 订单状态:"unpaid"(未支付)、"paid"(已支付)、"shipped"(已发货)、"received"(已签收)
    vector<OrderStatusObserver*> observers;
    mutex mtx;
public:
    Order(const string& id) : orderId(id), status("unpaid") {}
    // 注册观察者(重写 Subject 接口)
    void RegisterObserver(Observer* observer) override {
        if (observer == nullptr) return;
        OrderStatusObserver* statusObserver = dynamic_cast<OrderStatusObserver*>(observer);
        if (statusObserver == nullptr) {
            throw invalid_argument("观察者类型不匹配!");
        }
        lock_guard<mutex> lock(mtx);
        auto iter = find(observers.begin(), observers.end(), statusObserver);
        if (iter == observers.end()) {
            observers.push_back(statusObserver);
            cout << "订单" << orderId << "注册观察者:" << statusObserver->GetObserverName() << endl;
        }
    }
    // 解注册观察者(重写 Subject 接口)
    void RemoveObserver(Observer* observer) override {
        OrderStatusObserver* statusObserver = dynamic_cast<OrderStatusObserver*>(observer);
        if (statusObserver == nullptr) return;
        lock_guard<mutex> lock(mtx);
        auto iter = find(observers.begin(), observers.end(), statusObserver);
        if (iter != observers.end()) {
            observers.erase(iter);
            cout << "订单" << orderId << "解注册观察者:" << statusObserver->GetObserverName() << endl;
        }
    }
    // 通知观察者(重写 Subject 接口)
    void NotifyObservers() override {
        lock_guard<mutex> lock(mtx);
        vector<OrderStatusObserver*> tempObservers = observers;
        string tempOrderId = orderId;
        string tempStatus = status;
        // 异步通知(避免阻塞订单状态更新)
        thread([tempObservers, tempOrderId, tempStatus]() {
            for (OrderStatusObserver* observer : tempObservers) {
                observer->OnStatusChanged(tempOrderId, tempStatus);
            }
        }).detach();
    }
    // 更新订单状态(触发通知)
    void UpdateStatus(const string& newStatus) {
        if (newStatus != "unpaid" && newStatus != "paid" && newStatus != "shipped" && newStatus != "received") {
            throw invalid_argument("无效的订单状态:" + newStatus);
        }
        lock_guard<mutex> lock(mtx);
        if (this->status == newStatus) {
            cout << "订单" << orderId << "状态未变化(当前:" << newStatus << ")" << endl;
            return;
        }
        this->status = newStatus;
        cout << "\n订单" << orderId << "状态更新为:" << newStatus << endl;
        NotifyObservers();
    }
    // 获取订单状态
    string GetStatus() const {
        lock_guard<mutex> lock(mtx);
        return status;
    }
    // 获取订单ID
    string GetOrderId() const {
        return orderId;
    }
};
// -------------------------- 部分2:策略模式(物流调度算法) --------------------------
// 策略抽象:调度策略
class DispatchStrategy {
public:
    virtual string Dispatch(const string& orderId, const string& orderType) const = 0;
    virtual string GetStrategyName() const = 0;
    virtual ~DispatchStrategy() {}
};
// 具体策略1:普通调度(适用于普通订单)
class NormalDispatchStrategy : public DispatchStrategy {
public:
    string Dispatch(const string& orderId, const string& orderType) const override {
        string result = "订单" + orderId + "(" + orderType + ")使用普通调度:分配常规物流车辆,预计3-5天送达";
        cout << result << endl;
        return result;
    }
    string GetStrategyName() const override {
        return "普通调度策略";
    }
};
// 具体策略2:优先调度(适用于加急订单)
class PriorityDispatchStrategy : public DispatchStrategy {
public:
    string Dispatch(const string& orderId, const string& orderType) const override {
        string result = "订单" + orderId + "(" + orderType + ")使用优先调度:分配加急物流车辆,预计1-2天送达";
        cout << result << endl;
        return result;
    }
    string GetStrategyName() const override {
        return "优先调度策略";
    }
};
// 具体策略3:大件调度(适用于大件订单)
class LargeItemDispatchStrategy : public DispatchStrategy {
public:
    string Dispatch(const string& orderId, const string& orderType) const override {
        string result = "订单" + orderId + "(" + orderType + ")使用大件调度:分配专用大件物流车辆,预计2-4天送达";
        cout << result << endl;
        return result;
    }
    string GetStrategyName() const override {
        return "大件调度策略";
    }
};
// 上下文:物流调度系统
class LogisticsDispatchSystem {
private:
    mutex mtx;
    // 策略映射:订单类型 -> 策略(支持动态配置)
    map<string, shared_ptr<DispatchStrategy>> strategyMap;
public:
    // 初始化默认策略
    LogisticsDispatchSystem() {
        strategyMap["normal"] = make_shared<NormalDispatchStrategy>();       // 普通订单
        strategyMap["express"] = make_shared<PriorityDispatchStrategy>();    // 加急订单
        strategyMap["large"] = make_shared<LargeItemDispatchStrategy>();     // 大件订单
    }
    // 注册新策略(支持动态新增)
    void RegisterStrategy(const string& orderType, shared_ptr<DispatchStrategy> strategy) {
        if (orderType.empty() || strategy == nullptr) {
            throw invalid_argument("订单类型或策略不能为空!");
        }
        lock_guard<mutex> lock(mtx);
        strategyMap[orderType] = strategy;
        cout << "注册新调度策略:订单类型=" << orderType << ",策略=" << strategy->GetStrategyName() << endl;
    }
    // 执行调度(根据订单类型选择策略)
    string DispatchOrder(const string& orderId, const string& orderType) {
        lock_guard<mutex> lock(mtx);
        auto iter = strategyMap.find(orderType);
        if (iter == strategyMap.end()) {
            throw invalid_argument("不支持的订单类型:" + orderType);
        }
        return iter->second->Dispatch(orderId, orderType);
    }
};
// -------------------------- 部分3:系统整合与客户端测试 --------------------------
// 系统整合:订单状态更新后触发调度
void OnOrderStatusUpdated(const shared_ptr<Order>& order, const shared_ptr<LogisticsDispatchSystem>& dispatchSystem, const string& orderType) {
    string status = order->GetStatus();
    if (status == "paid") {  // 订单已支付,触发调度
        cout << "\n--- 订单" << order->GetOrderId() << "已支付,开始物流调度 ---" << endl;
        dispatchSystem->DispatchOrder(order->GetOrderId(), orderType);
    }
}
int main() {
    try {
        // 1. 创建物流调度系统
        auto dispatchSystem = make_shared<LogisticsDispatchSystem>();
        // 2. 创建观察者
        auto customerApp = make_shared<CustomerAppObserver>();
        auto merchantBackend = make_shared<MerchantBackendObserver>();
        auto logisticsSystem = make_shared<LogisticsSystemObserver>();
        // 3. 创建订单(普通订单、加急订单)
        auto normalOrder = make_shared<Order>("ORD20240501001");  // 普通订单
        auto expressOrder = make_shared<Order>("ORD20240501002"); // 加急订单
        // 4. 为订单注册观察者
        normalOrder->RegisterObserver(customerApp.get());
        normalOrder->RegisterObserver(merchantBackend.get());
        normalOrder->RegisterObserver(logisticsSystem.get());
        expressOrder->RegisterObserver(customerApp.get());
        expressOrder->RegisterObserver(merchantBackend.get());
        expressOrder->RegisterObserver(logisticsSystem.get());
        // 5. 模拟普通订单流程:未支付 -> 已支付(触发调度)-> 已发货 -> 已签收
        cout << "=== 普通订单流程 ===" << endl;
        normalOrder->UpdateStatus("paid");  // 已支付,触发调度
        this_thread::sleep_for(chrono::seconds(1));  // 等待异步通知完成
        OnOrderStatusUpdated(normalOrder, dispatchSystem, "normal");
        normalOrder->UpdateStatus("shipped");  // 已发货
        this_thread::sleep_for(chrono::seconds(1));
        normalOrder->UpdateStatus("received");  // 已签收
        this_thread::sleep_for(chrono::seconds(1));
        // 6. 模拟加急订单流程
        cout << "\n=== 加急订单流程 ===" << endl;
        expressOrder->UpdateStatus("paid");  // 已支付,触发调度
        this_thread::sleep_for(chrono::seconds(1));
        OnOrderStatusUpdated(expressOrder, dispatchSystem, "express");
        // 7. 新增冷链物流策略(扩展功能)
        class ColdChainDispatchStrategy : public DispatchStrategy {
        public:
            string Dispatch(const string& orderId, const string& orderType) const override {
                return "订单" + orderId + "(" + orderType + ")使用冷链调度:分配冷藏物流车辆,预计1-2天送达";
            }
            string GetStrategyName() const override { return "冷链调度策略"; }
        };
        dispatchSystem->RegisterStrategy("cold_chain", make_shared<ColdChainDispatchStrategy>());
        // 8. 测试新增的冷链订单
        auto coldChainOrder = make_shared<Order>("ORD20240501003");
        coldChainOrder->RegisterObserver(customerApp.get());
        cout << "\n=== 冷链订单流程 ===" << endl;
        coldChainOrder->UpdateStatus("paid");
        this_thread::sleep_for(chrono::seconds(1));
        OnOrderStatusUpdated(coldChainOrder, dispatchSystem, "cold_chain");
    } catch (const exception& e) {
        cout << "系统错误:" << e.what() << endl;
    }
    return 0;
}

18.5.4 代码说明与运行效果

核心设计亮点

运行结果(关键片段)

订单ORD20240501001注册观察者:客户 App
订单ORD20240501001注册观察者:商家后台
订单ORD20240501001注册观察者:物流系统
...
=== 普通订单流程 ===

订单ORD20240501001状态更新为:paid
[客户 App] 订单ORD20240501001状态更新为:paid,已推送通知给客户
[商家后台] 订单ORD20240501001状态更新为:paid,已同步至商家管理系统
[物流系统] 订单ORD20240501001状态更新为:paid,已触发物流调度

--- 订单ORD20240501001已支付,开始物流调度 ---
订单ORD20240501001(normal)使用普通调度:分配常规物流车辆,预计3-5天送达
...
=== 冷链订单流程 ===
注册新调度策略:订单类型=cold_chain,策略=冷链调度策略

订单ORD20240501003状态更新为:paid
[客户 App] 订单ORD20240501003状态更新为:paid,已推送通知给客户

--- 订单ORD20240501003已支付,开始物流调度 ---
订单ORD20240501003(cold_chain)使用冷链调度:分配冷藏物流车辆,预计1-2天送达

18.6 本章总结

本章重点讲解了 C++ 开发中常用的两种行为型设计模式:观察者模式和策略模式,核心要点总结如下:

通过本章学习,你应能熟练运用观察者模式和策略模式解决实际开发中的“交互通知”和“算法切换”问题,结合两种模式的组合使用,编写高内聚、低耦合、易扩展的 C++ 代码。后续章节将继续讲解其他行为型设计模式(如装饰者模式、适配器模式),进一步提升你的代码设计能力。

到此这篇关于C++中观察者模式与策略模式的实战指南的文章就介绍到这了,更多相关C++观察者模式与策略模式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文