1. C++策略模式实战:从理论到代码的完整指南
策略模式是面向对象设计中解决算法频繁变更问题的经典方案。我在实际开发中多次使用策略模式重构过支付系统、游戏AI和数据处理模块,它能让代码在面对业务需求变化时保持惊人的弹性。今天就用一个电商折扣系统的案例,带你彻底掌握C++中策略模式的实现精髓。
2. 策略模式核心思想解析
2.1 模式定义与适用场景
策略模式(Strategy Pattern)属于行为型设计模式,其核心思想是将算法族定义为一组可互相替换的策略类,使得算法可以独立于使用它的客户端变化。当你的代码中出现以下信号时,就该考虑引入策略模式了:
- 系统中存在多个条件分支处理同一类问题(如不同用户等级对应不同折扣算法)
- 需要动态切换算法实现(如运行时根据配置切换排序策略)
- 算法实现可能频繁变更或扩展(如支付方式持续增加)
重要提示:不要为了使用模式而强行套用,当简单if-else就能清晰表达且预计不会扩展时,策略模式反而会增加不必要的复杂度。
2.2 UML类图与角色划分
标准策略模式包含三个核心角色:
- Context(环境类):持有一个策略对象的引用,通过接口调用具体策略
- Strategy(抽象策略):定义所有支持的算法的公共接口
- ConcreteStrategy(具体策略):实现抽象策略定义的接口
mermaid复制classDiagram
class Context {
-strategy: Strategy*
+setStrategy(Strategy*)
+executeStrategy()
}
class Strategy {
<<interface>>
+execute()
}
class ConcreteStrategyA {
+execute()
}
class ConcreteStrategyB {
+execute()
}
Context o--> Strategy
Strategy <|-- ConcreteStrategyA
Strategy <|-- ConcreteStrategyB
3. C++实现电商折扣系统实战
3.1 基础实现版本
假设我们要为电商平台实现一个折扣计算系统,包含普通用户、VIP用户和超级VIP三种折扣策略。先看最基础的实现:
cpp复制// 抽象策略类
class DiscountStrategy {
public:
virtual ~DiscountStrategy() = default;
virtual double calculate(double price) = 0;
};
// 具体策略类
class NormalDiscount : public DiscountStrategy {
public:
double calculate(double price) override {
return price; // 无折扣
}
};
class VIPDiscount : public DiscountStrategy {
public:
double calculate(double price) override {
return price * 0.9; // 9折
}
};
class SuperVIPDiscount : public DiscountStrategy {
public:
double calculate(double price) override {
return price * 0.7; // 7折
}
};
// 环境类
class DiscountContext {
private:
DiscountStrategy* strategy;
public:
explicit DiscountContext(DiscountStrategy* strategy = nullptr)
: strategy(strategy) {}
void setStrategy(DiscountStrategy* newStrategy) {
strategy = newStrategy;
}
double applyDiscount(double price) {
if (!strategy) return price;
return strategy->calculate(price);
}
};
使用示例:
cpp复制DiscountContext context;
NormalDiscount normal;
VIPDiscount vip;
context.setStrategy(&normal);
cout << "Normal price: " << context.applyDiscount(100) << endl;
context.setStrategy(&vip);
cout << "VIP price: " << context.applyDiscount(100) << endl;
3.2 使用智能指针改进
原始指针管理存在内存泄漏风险,改用unique_ptr更安全:
cpp复制class DiscountContext {
private:
std::unique_ptr<DiscountStrategy> strategy;
public:
explicit DiscountContext(std::unique_ptr<DiscountStrategy>&& strategy = nullptr)
: strategy(std::move(strategy)) {}
void setStrategy(std::unique_ptr<DiscountStrategy>&& newStrategy) {
strategy = std::move(newStrategy);
}
// 其他方法不变
};
3.3 策略工厂模式集成
当策略需要根据配置动态创建时,可以结合工厂模式:
cpp复制class DiscountFactory {
public:
static std::unique_ptr<DiscountStrategy> create(const std::string& type) {
if (type == "normal") return std::make_unique<NormalDiscount>();
if (type == "vip") return std::make_unique<VIPDiscount>();
if (type == "supervip") return std::make_unique<SuperVIPDiscount>();
throw std::invalid_argument("Unknown discount type");
}
};
// 使用示例
auto strategy = DiscountFactory::create("vip");
DiscountContext context(std::move(strategy));
4. 高级应用技巧
4.1 策略模式与模板的结合
对于性能敏感场景,可以用模板策略替代虚函数:
cpp复制template<typename T>
class DiscountContext {
private:
T strategy;
public:
double applyDiscount(double price) {
return strategy.calculate(price);
}
};
// 使用示例
DiscountContext<VIPDiscount> context;
cout << context.applyDiscount(100) << endl;
4.2 策略模式的Lambda表达式实现
C++11后可以用函数对象替代策略类:
cpp复制class DiscountContext {
private:
std::function<double(double)> strategy;
public:
explicit DiscountContext(std::function<double(double)> strategy = nullptr)
: strategy(strategy) {}
double applyDiscount(double price) {
return strategy ? strategy(price) : price;
}
};
// 使用示例
DiscountContext context([](double p) { return p * 0.8; });
cout << context.applyDiscount(100) << endl;
4.3 策略模式的线程安全考虑
多线程环境下共享策略对象时需要注意:
cpp复制class DiscountContext {
private:
std::mutex mtx;
std::shared_ptr<DiscountStrategy> strategy;
public:
void setStrategy(std::shared_ptr<DiscountStrategy> newStrategy) {
std::lock_guard<std::mutex> lock(mtx);
strategy = newStrategy;
}
double applyDiscount(double price) {
std::shared_ptr<DiscountStrategy> localStrategy;
{
std::lock_guard<std::mutex> lock(mtx);
localStrategy = strategy;
}
return localStrategy ? localStrategy->calculate(price) : price;
}
};
5. 实战中的典型问题与解决方案
5.1 策略对象生命周期管理
常见错误:在策略对象被销毁后仍然被Context使用。
解决方案:
- 使用shared_ptr共享所有权
- 确保Context生命周期不超过策略对象
- 在Context析构时取消策略引用
5.2 策略组合问题
当需要组合多个策略时(如先打折再满减):
cpp复制class CombinedDiscount : public DiscountStrategy {
private:
std::vector<std::shared_ptr<DiscountStrategy>> strategies;
public:
void addStrategy(std::shared_ptr<DiscountStrategy> strategy) {
strategies.push_back(strategy);
}
double calculate(double price) override {
for (auto& s : strategies) {
price = s->calculate(price);
}
return price;
}
};
5.3 策略模式与状态模式的区别
新手常混淆这两种模式,关键区别在于:
- 策略模式:算法实现不同,但目标一致(如不同折扣方式最终都返回折扣价)
- 状态模式:行为随状态本质变化(如订单状态从"待支付"变为"已发货")
6. 性能优化与最佳实践
6.1 虚函数调用开销分析
虚函数调用相比普通函数有额外开销:
- 通过虚表指针间接跳转
- 无法内联优化
- 分支预测失败惩罚
优化方案:
- 对性能关键路径使用模板策略
- 将小函数声明为final允许编译器优化
- 批量处理数据减少调用次数
6.2 策略对象复用
频繁创建销毁策略对象时:
- 使用对象池缓存常用策略
- 设计无状态的策略类(所有数据通过参数传入)
- 对线程安全策略使用shared_ptr共享
6.3 设计原则检查清单
优秀策略模式实现应满足:
- [ ] 策略接口足够抽象,不包含实现细节
- [ ] Context不依赖具体策略类
- [ ] 新增策略不需修改已有代码
- [ ] 策略类之间相互独立
- [ ] 命名清晰体现策略用途
7. 现代C++中的演进实现
7.1 使用std::variant实现策略模式
C++17引入的variant可以实现静态策略选择:
cpp复制using DiscountStrategies = std::variant<NormalDiscount, VIPDiscount, SuperVIPDiscount>;
class DiscountContext {
private:
DiscountStrategies strategy;
public:
template<typename T>
explicit DiscountContext(T&& s) : strategy(std::forward<T>(s)) {}
double applyDiscount(double price) {
return std::visit([price](auto&& s) {
return s.calculate(price);
}, strategy);
}
};
7.2 策略模式与概念(Concepts)
C++20概念可以约束策略类型:
cpp复制template<typename T>
concept DiscountStrategy = requires(T t, double price) {
{ t.calculate(price) } -> std::convertible_to<double>;
};
template<DiscountStrategy T>
class DiscountContext {
// 实现同上
};
8. 实际项目经验分享
在电商促销系统中,我们曾用策略模式管理超过30种促销规则。几点关键经验:
- 策略配置化:将策略类型和参数存入数据库,支持动态加载
- 策略优先级:实现策略组合时的冲突解决机制
- 策略监控:记录各策略的执行耗时和效果
- 策略版本控制:支持策略灰度发布和回滚
一个典型的策略配置JSON示例:
json复制{
"strategy": "CompositeDiscount",
"children": [
{
"strategy": "VIPDiscount",
"params": {"rate": 0.9}
},
{
"strategy": "Full100Minus20",
"params": {"threshold": 100, "discount": 20}
}
]
}
9. 测试策略模式的最佳实践
9.1 单元测试策略类
每个策略类应该独立测试:
cpp复制TEST(VIPDiscountTest, ShouldApply10PercentOff) {
VIPDiscount strategy;
EXPECT_DOUBLE_EQ(90.0, strategy.calculate(100.0));
}
9.2 模拟策略测试Context
使用Mock策略测试Context行为:
cpp复制class MockDiscount : public DiscountStrategy {
public:
MOCK_METHOD(double, calculate, (double), (override));
};
TEST(DiscountContextTest, ShouldDelegateToStrategy) {
MockDiscount mock;
EXPECT_CALL(mock, calculate(100.0)).WillOnce(Return(80.0));
DiscountContext context(&mock);
EXPECT_DOUBLE_EQ(80.0, context.applyDiscount(100.0));
}
9.3 性能测试策略切换开销
测量策略切换的耗时影响:
cpp复制BENCHMARK("StrategySwitch", [](benchmark::State& state) {
DiscountContext context;
NormalDiscount normal;
VIPDiscount vip;
for (auto _ : state) {
context.setStrategy(&normal);
context.setStrategy(&vip);
}
});
10. 扩展思考:策略模式的变体
10.1 策略模式与桥接模式结合
当抽象和实现都需要扩展时:
cpp复制// 抽象部分
class Payment {
protected:
DiscountStrategy* discount; // 桥接策略
public:
virtual ~Payment() = default;
virtual void pay(double amount) = 0;
void setDiscount(DiscountStrategy* d) { discount = d; }
};
// 实现部分
class CreditCardPayment : public Payment {
public:
void pay(double amount) override {
double finalAmount = discount ? discount->calculate(amount) : amount;
// 信用卡支付逻辑
}
};
10.2 策略模式与命令模式融合
将策略封装为可撤销的命令:
cpp复制class DiscountCommand {
private:
DiscountStrategy* strategy;
double originalAmount;
public:
DiscountCommand(DiscountStrategy* s, double amt)
: strategy(s), originalAmount(amt) {}
double execute() { return strategy->calculate(originalAmount); }
double undo() { return originalAmount; }
};
10.3 策略模式在游戏开发中的应用
游戏AI中的行为策略示例:
cpp复制class AICharacter {
private:
std::unique_ptr<BehaviorStrategy> behavior;
public:
void update() {
if (behavior) behavior->execute();
}
void setBehavior(std::unique_ptr<BehaviorStrategy>&& newBehavior) {
behavior = std::move(newBehavior);
}
};
class AggressiveBehavior : public BehaviorStrategy {
void execute() override {
// 攻击最近玩家
}
};
