1. 策略模式在C++中的核心价值
策略模式是我在大型C++项目中频繁使用的设计模式之一。它的本质是将算法家族封装成独立的类,使得它们可以相互替换。这种模式让算法的变化独立于使用算法的客户,特别适合处理那些需要根据不同条件切换不同业务规则的场景。
想象一下你在开发一个电商系统,需要支持多种折扣策略:新用户首单优惠、会员折扣、满减活动等。如果把这些折扣逻辑全部硬编码在订单类里,每次新增折扣类型都要修改原有代码。而策略模式通过将每种折扣算法封装成独立策略类,完美解决了这个问题。
2. 策略模式的经典实现结构
2.1 基础类关系设计
标准的策略模式包含三个核心组件:
- Strategy(策略接口):定义所有支持的算法的公共接口
- ConcreteStrategy(具体策略):实现Strategy接口的具体算法类
- Context(上下文):持有一个策略对象的引用,通过策略接口与具体策略交互
cpp复制// 策略接口
class DiscountStrategy {
public:
virtual ~DiscountStrategy() = default;
virtual double applyDiscount(double price) = 0;
};
// 具体策略:新用户折扣
class NewUserDiscount : public DiscountStrategy {
public:
double applyDiscount(double price) override {
return price * 0.9; // 打9折
}
};
// 上下文类
class Order {
private:
std::unique_ptr<DiscountStrategy> strategy_;
public:
void setStrategy(std::unique_ptr<DiscountStrategy>&& strategy) {
strategy_ = std::move(strategy);
}
double calculateFinalPrice(double originalPrice) {
if(strategy_) {
return strategy_->applyDiscount(originalPrice);
}
return originalPrice;
}
};
2.2 运行时策略切换
策略模式最强大的特性是允许在运行时动态切换算法:
cpp复制Order order;
order.setStrategy(std::make_unique<NewUserDiscount>());
double price1 = order.calculateFinalPrice(100); // 90元
// 切换到会员折扣策略
order.setStrategy(std::make_unique<VIPDiscount>());
double price2 = order.calculateFinalPrice(100); // 80元
3. 现代C++中的策略模式优化
3.1 使用std::function替代继承
在C++11及以后版本中,我们可以用函数对象简化策略模式:
cpp复制class Order {
public:
using DiscountStrategy = std::function<double(double)>;
void setStrategy(DiscountStrategy strategy) {
strategy_ = strategy;
}
private:
DiscountStrategy strategy_;
};
// 使用lambda表达式定义策略
order.setStrategy([](double price) { return price * 0.8; });
3.2 策略对象的生命周期管理
在C++中需要特别注意策略对象的内存管理:
- 对于短期使用的策略,可以考虑值语义
- 长期持有的策略建议使用智能指针
- 线程安全场景下需要加锁或使用无状态策略
4. 实战中的典型应用场景
4.1 游戏开发中的AI行为
在游戏NPC AI中,不同的战斗策略可以随时切换:
cpp复制class EnemyAI {
public:
void setBehavior(std::unique_ptr<AIStrategy>&& strategy) {
// 线程安全处理
std::lock_guard<std::mutex> lock(mutex_);
strategy_ = std::move(strategy);
}
void update() {
if(strategy_) {
strategy_->execute();
}
}
private:
std::mutex mutex_;
std::unique_ptr<AIStrategy> strategy_;
};
4.2 金融系统中的风险计算
不同金融产品需要采用不同的风险评估算法:
cpp复制RiskCalculator calculator;
if(product.type == "Stock") {
calculator.setStrategy(std::make_unique<BlackScholesModel>());
} else if(product.type == "Bond") {
calculator.setStrategy(std::make_unique<DurationAnalysis>());
}
double risk = calculator.calculate(product);
5. 性能优化与设计考量
5.1 虚函数调用的开销
策略模式通过虚函数实现多态,可能带来性能影响。在性能敏感场景可以考虑:
- 使用模板策略模式(编译期多态)
- 将小策略实现为内联函数
- 使用策略缓存减少对象创建
cpp复制template<typename Strategy>
class Context {
public:
void execute() {
Strategy::apply();
}
};
5.2 策略模式的边界
不是所有算法都适合用策略模式:
- 算法非常简单且不会变化时,直接实现更高效
- 策略之间需要共享大量状态时,可能增加复杂度
- 算法需要访问上下文私有成员时,可能需要friend声明
6. 测试与维护建议
6.1 单元测试策略
每个具体策略类应该独立测试:
cpp复制TEST(VIPDiscountTest, ShouldApply20PercentDiscount) {
VIPDiscount strategy;
EXPECT_DOUBLE_EQ(80.0, strategy.applyDiscount(100.0));
}
6.2 避免策略膨胀
当策略类过多时,可以考虑:
- 使用工厂方法管理策略创建
- 将相关策略组合成策略组
- 引入策略的默认实现
7. 与其他模式的关系
策略模式常与其他模式配合使用:
- 与工厂模式结合:动态创建策略对象
- 与装饰器模式结合:组合多个策略
- 与享元模式结合:共享无状态策略
在最近的一个交易系统项目中,我结合策略模式和抽象工厂,实现了可插拔的交易算法模块,使系统能够在不重启的情况下热更新交易策略。
