1. 策略模式基础与变体需求
在C++开发中遇到需要动态切换算法或行为的场景时,策略模式(Strategy Pattern)是最常用的设计模式之一。传统策略模式通过定义算法接口、实现具体策略类和使用上下文类来解耦算法实现,但在实际工程中我们常常需要更灵活的变体。
我经历过一个电商促销系统项目,最初采用经典策略模式实现折扣计算。随着业务复杂化,出现了以下痛点:
- 需要支持策略的运行时动态组合(如满减+折扣)
- 某些策略需要访问上下文中的特定数据
- 策略的创建逻辑变得复杂,存在依赖注入需求
这些问题促使我深入研究策略模式的几种实用变体。经过多个项目验证,下面分享三种最具工程价值的改进方案。
2. 三种核心变体实现方案
2.1 策略组合模式
当需要同时应用多个策略时,可以采用组合模式的思想:
class CompositeStrategy : public IPriceStrategy { public: void addStrategy(std::shared_ptr<IPriceStrategy> strategy) { strategies_.push_back(strategy); } double calculate(double price) override { for (auto& strategy : strategies_) { price = strategy->calculate(price); } return price; } private: std::vector<std::shared_ptr<IPriceStrategy>> strategies_; };关键实现细节:
- 使用智能指针管理策略生命周期
- 按添加顺序依次应用策略
- 可通过优先级字段控制执行顺序
实际项目中曾遇到组合策略的循环引用问题,建议在addStrategy时做环路检测
2.2 上下文感知策略
某些策略需要访问上下文状态时,可以改进接口设计:
class IContextAwareStrategy { public: virtual void execute(OrderContext& context) = 0; }; class FreeShippingStrategy : public IContextAwareStrategy { public: void execute(OrderContext& context) override { if (context.getTotalWeight() < 10.0) { context.setShippingFee(0); } } };典型应用场景:
- 需要订单历史数据
- 依赖用户等级信息
- 基于地理位置的计算
2.3 策略工厂进阶用法
复杂场景下策略创建需要更多控制:
class StrategyFactory { public: static std::unique_ptr<IStrategy> create(const std::string& type) { auto it = creators_.find(type); if (it != creators_.end()) { return it->second(); } throw std::runtime_error("Unknown strategy type"); } template <typename T> static void registerCreator(const std::string& type) { creators_[type] = []() { return std::make_unique<T>(); }; } private: static std::unordered_map< std::string, std::function<std::unique_ptr<IStrategy>()> > creators_; };优势包括:
- 支持运行时动态注册策略
- 解耦策略实现与使用
- 方便单元测试mock
3. 性能优化与工程实践
3.1 内存管理方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 裸指针 | 零开销 | 易内存泄漏 | 简单短期任务 |
| unique_ptr | 自动释放 | 不能复制 | 多数场景 |
| shared_ptr | 共享所有权 | 循环引用风险 | 需要策略共享时 |
3.2 线程安全实现
多线程环境下策略模式需要注意:
class ThreadSafeStrategy { public: virtual double calculate(double price) { std::lock_guard<std::mutex> lock(mutex_); // 具体计算逻辑 } private: std::mutex mutex_; };经验表明,对于无状态策略可以避免加锁,提升性能
3.3 实际项目中的取舍
在物流路径规划系统中,我们最终采用的混合方案:
- 核心算法用unique_ptr管理
- 可插拔模块用shared_ptr
- 策略组合支持动态配置
- 上下文数据采用immutable模式
4. 典型问题与解决方案
4.1 策略选择逻辑膨胀
反例:
if (user.isVIP()) { if (order.total > 1000) { strategy = new VipBigOrderStrategy(); } else { strategy = new VipNormalStrategy(); } } else { // 更多if-else... }改进方案:
- 使用策略工厂+配置表
- 引入规则引擎
- 采用责任链模式预处理
4.2 策略接口版本升级
向后兼容方案:
class IStrategyV2 : public IStrategyV1 { public: virtual void newFeature() = 0; }; class AdapterStrategy : public IStrategyV2 { public: explicit AdapterStrategy(std::shared_ptr<IStrategyV1> old) : old_(old) {} void legacyMethod() override { old_->legacyMethod(); } void newFeature() override { // 默认实现或转换逻辑 } private: std::shared_ptr<IStrategyV1> old_; };4.3 调试复杂策略系统
实用调试技巧:
- 为策略添加唯一ID和版本号
- 实现策略的序列化接口
- 在上下文保存策略应用历史
- 使用装饰器模式添加日志
5. 现代C++特性应用
5.1 使用std::function替代接口
using Strategy = std::function<double(double)>; Strategy s1 = [](double p) { return p * 0.9; }; Strategy s2 = [](double p) { return p > 100 ? p - 20 : p; }; double price = s2(s1(original_price));适用场景:
- 简单策略逻辑
- 需要快速原型开发
- 策略作为参数传递
5.2 可变参数模板策略
template <typename... Strategies> class VariadicStrategy { public: double apply(double price) { return (Strategies::apply(price), ...); } }; // 使用 using MyStrategy = VariadicStrategy<Discount, Coupon, Tax>;5.3 编译期策略选择
template <typename T> struct StrategySelector { static constexpr bool useOptimized = std::is_arithmetic_v<typename T::value_type>; using type = std::conditional_t< useOptimized, OptimizedStrategy<T>, GeneralStrategy<T> >; };在性能敏感系统中,这种零开销抽象能带来显著提升。一个实际测试案例显示,编译期策略选择比运行时动态分配快3-5倍。