1. 策略模式基础回顾与核心价值
策略模式作为行为型设计模式的代表,其核心思想是将算法族封装成独立的类,使得它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端,完美体现了"开闭原则"的精髓。
在C++中实现策略模式通常包含三个关键角色:
- Strategy(策略接口):定义所有支持的算法族的公共接口
- ConcreteStrategy(具体策略):实现策略接口的具体算法类
- Context(上下文):持有一个策略对象的引用,通过策略接口与之交互
cpp复制// 策略接口
class SortingStrategy {
public:
virtual ~SortingStrategy() = default;
virtual void sort(vector<int>& data) = 0;
};
// 具体策略A:快速排序
class QuickSort : public SortingStrategy {
public:
void sort(vector<int>& data) override {
cout << "Using quick sort" << endl;
// 快速排序实现...
}
};
// 具体策略B:归并排序
class MergeSort : public SortingStrategy {
public:
void sort(vector<int>& data) override {
cout << "Using merge sort" << endl;
// 归并排序实现...
}
};
// 上下文
class SortContext {
private:
unique_ptr<SortingStrategy> strategy;
public:
void setStrategy(unique_ptr<SortingStrategy>&& newStrategy) {
strategy = move(newStrategy);
}
void executeSort(vector<int>& data) {
if(strategy) {
strategy->sort(data);
}
}
};
提示:在C++中实现策略模式时,使用智能指针(如unique_ptr)管理策略对象生命周期可以避免内存泄漏问题,特别是在运行时动态切换策略的场景下。
策略模式的核心价值在于:
- 消除条件语句:避免在业务逻辑中出现大量的if-else或switch-case语句
- 提高扩展性:新增算法策略时无需修改现有代码
- 运行时灵活性:算法可以在运行时动态切换
- 职责分离:将算法实现与使用环境解耦
2. 高级应用场景与性能优化
2.1 策略组合与复合策略
在实际工程中,单一策略往往不能满足复杂需求。我们可以通过策略组合的方式构建更强大的解决方案。比如在游戏AI中,可能需要组合移动策略和攻击策略:
cpp复制class MovementStrategy {
public:
virtual void move(Character& character) = 0;
};
class AttackStrategy {
public:
virtual void attack(Character& character) = 0;
};
class CompositeStrategy {
private:
unique_ptr<MovementStrategy> moveStrategy;
unique_ptr<AttackStrategy> attackStrategy;
public:
CompositeStrategy(unique_ptr<MovementStrategy>&& ms,
unique_ptr<AttackStrategy>&& as)
: moveStrategy(move(ms)), attackStrategy(move(as)) {}
void execute(Character& character) {
moveStrategy->move(character);
attackStrategy->attack(character);
}
};
2.2 基于模板的策略模式
传统策略模式通过虚函数实现多态,会带来一定的运行时开销。对于性能敏感的场景,可以使用模板实现编译期策略选择:
cpp复制template<typename TStrategy>
class Context {
private:
TStrategy strategy;
public:
template<typename... Args>
void execute(Args&&... args) {
strategy.execute(forward<Args>(args)...);
}
};
// 具体策略类不需要继承公共接口
struct FastStrategy {
void execute(int param) {
// 高性能实现...
}
};
struct SafeStrategy {
void execute(int param) {
// 安全优先的实现...
}
};
// 使用示例
Context<FastStrategy> fastContext;
fastContext.execute(42);
这种方式的优势:
- 零虚函数开销
- 编译期策略绑定,更好的编译器优化
- 更强的类型安全性
缺点则是失去了运行时的灵活性。
2.3 策略缓存与对象池
频繁创建销毁策略对象可能导致性能问题。对于小型策略对象,可以考虑使用对象池模式:
cpp复制class StrategyPool {
private:
static constexpr size_t POOL_SIZE = 10;
array<unique_ptr<Strategy>, POOL_SIZE> pool;
size_t index = 0;
public:
Strategy* acquire(StrategyType type) {
if(index >= POOL_SIZE) index = 0;
if(!pool[index] || pool[index]->getType() != type) {
pool[index] = createStrategy(type);
}
return pool[index++].get();
}
private:
unique_ptr<Strategy> createStrategy(StrategyType type) {
switch(type) {
case StrategyType::A: return make_unique<StrategyA>();
case StrategyType::B: return make_unique<StrategyB>();
default: return nullptr;
}
}
};
3. 工程实践中的典型应用
3.1 支付系统策略实现
电商平台的支付系统是策略模式的经典应用场景。考虑支持多种支付方式的情况:
cpp复制class PaymentStrategy {
public:
virtual bool pay(double amount) = 0;
virtual ~PaymentStrategy() = default;
};
class CreditCardPayment : public PaymentStrategy {
public:
bool pay(double amount) override {
cout << "Processing credit card payment: " << amount << endl;
// 实际支付逻辑...
return true;
}
};
class PayPalPayment : public PaymentStrategy {
public:
bool pay(double amount) override {
cout << "Processing PayPal payment: " << amount << endl;
// 实际支付逻辑...
return true;
}
};
class PaymentProcessor {
private:
unique_ptr<PaymentStrategy> strategy;
public:
void setStrategy(unique_ptr<PaymentStrategy>&& newStrategy) {
strategy = move(newStrategy);
}
bool processPayment(double amount) {
if(strategy) {
return strategy->pay(amount);
}
return false;
}
};
在实际工程中,还需要考虑:
- 支付策略的线程安全性
- 支付结果的回调处理
- 策略的异常处理机制
- 支付策略的元信息(图标、描述等)
3.2 游戏AI行为策略
游戏开发中,NPC的行为AI经常使用策略模式实现不同行为模式的切换:
cpp复制class AIBehavior {
public:
virtual void update(NPC& npc) = 0;
virtual ~AIBehavior() = default;
};
class AggressiveBehavior : public AIBehavior {
public:
void update(NPC& npc) override {
// 攻击最近的玩家
auto target = findNearestEnemy(npc.position);
npc.moveTo(target.position);
npc.attack(target);
}
};
class DefensiveBehavior : public AIBehavior {
public:
void update(NPC& npc) override {
// 保持距离并治疗
if(npc.health < 50) {
npc.usePotion();
} else {
npc.keepDistanceFromEnemies();
}
}
};
class NPCAI {
private:
unique_ptr<AIBehavior> currentBehavior;
unordered_map<BehaviorType, unique_ptr<AIBehavior>> behaviorMap;
public:
NPCAI() {
// 预初始化所有行为策略
behaviorMap[BehaviorType::Aggressive] = make_unique<AggressiveBehavior>();
behaviorMap[BehaviorType::Defensive] = make_unique<DefensiveBehavior>();
}
void setBehavior(BehaviorType type) {
auto it = behaviorMap.find(type);
if(it != behaviorMap.end()) {
currentBehavior = it->second->clone();
}
}
void update(NPC& npc) {
if(currentBehavior) {
currentBehavior->update(npc);
}
}
};
注意:在游戏开发中,策略对象的克隆能力很重要,因为同一个策略类可能被多个NPC实例共享,但每个NPC可能需要独立的状态。
3.3 编译器优化策略
现代编译器通常提供多种优化级别,这本质上就是策略模式的应用:
cpp复制class OptimizationStrategy {
public:
virtual void optimize(IRModule& module) = 0;
};
class O0Strategy : public OptimizationStrategy {
public:
void optimize(IRModule& module) override {
// 无优化
}
};
class O1Strategy : public OptimizationStrategy {
public:
void optimize(IRModule& module) override {
// 基本优化:常量传播、死代码消除等
}
};
class O2Strategy : public OptimizationStrategy {
public:
void optimize(IRModule& module) override {
// 更激进优化:内联、循环展开等
}
};
class Compiler {
private:
unique_ptr<OptimizationStrategy> optStrategy;
public:
void setOptLevel(OptLevel level) {
switch(level) {
case OptLevel::O0: optStrategy = make_unique<O0Strategy>(); break;
case OptLevel::O1: optStrategy = make_unique<O1Strategy>(); break;
case OptLevel::O2: optStrategy = make_unique<O2Strategy>(); break;
}
}
void compile(SourceCode& code) {
// 前端处理...
IRModule ir = generateIR(code);
// 中端优化
if(optStrategy) {
optStrategy->optimize(ir);
}
// 后端代码生成...
}
};
4. 高级技巧与最佳实践
4.1 策略工厂与动态注册
大型项目中,策略类可能分散在不同模块,使用工厂模式配合动态注册可以解耦策略的创建:
cpp复制class StrategyFactory {
private:
using Creator = function<unique_ptr<Strategy>()>;
static unordered_map<string, Creator> registry;
public:
static unique_ptr<Strategy> create(const string& name) {
auto it = registry.find(name);
if(it != registry.end()) {
return it->second();
}
return nullptr;
}
static bool registerStrategy(const string& name, Creator creator) {
return registry.emplace(name, creator).second;
}
};
// 宏简化策略注册
#define REGISTER_STRATEGY(name, class) \
bool _registered_##class = \
StrategyFactory::registerStrategy(name, []{ \
return make_unique<class>(); \
})
// 策略类定义
class AdvancedStrategy : public Strategy {
// 实现...
};
// 在策略类的cpp文件中注册
REGISTER_STRATEGY("advanced", AdvancedStrategy);
这种方式的优势:
- 策略类可以分散在不同编译单元
- 新增策略无需修改工厂代码
- 支持插件式架构
4.2 策略与状态模式的结合
策略模式常与状态模式混淆,但它们有本质区别:
- 策略模式:客户端主动选择算法
- 状态模式:状态转换由内部条件触发
在某些场景下,可以结合两者优势:
cpp复制class StatefulStrategy : public Strategy {
protected:
unique_ptr<State> currentState;
public:
void setState(unique_ptr<State>&& newState) {
currentState = move(newState);
}
void execute() override {
if(currentState) {
currentState->handle(*this);
}
}
};
class State {
public:
virtual void handle(StatefulStrategy& context) = 0;
};
class InitialState : public State {
public:
void handle(StatefulStrategy& context) override {
// 处理逻辑...
if(needTransition()) {
context.setState(make_unique<NextState>());
}
}
};
4.3 策略模式的测试策略
策略模式天然支持更好的可测试性,可以针对每个策略单独测试:
cpp复制TEST(QuickSortStrategyTest, SortsNumbersCorrectly) {
QuickSort strategy;
vector<int> data = {3, 1, 4, 1, 5};
vector<int> expected = {1, 1, 3, 4, 5};
strategy.sort(data);
ASSERT_EQ(data, expected);
}
TEST(MergeSortStrategyTest, HandlesEmptyInput) {
MergeSort strategy;
vector<int> data;
vector<int> expected;
strategy.sort(data);
ASSERT_EQ(data, expected);
}
// 上下文测试可以mock策略
TEST(SortContextTest, DelegatesToStrategy) {
MockStrategy mockStrategy;
EXPECT_CALL(mockStrategy, sort(_)).Times(1);
SortContext context;
context.setStrategy(make_unique<MockStrategyWrapper>(&mockStrategy));
vector<int> data = {1, 2, 3};
context.executeSort(data);
}
测试时的最佳实践:
- 每个具体策略应有独立的测试套件
- 上下文测试主要验证委托关系
- 使用mock对象测试策略切换逻辑
- 边界条件测试应在策略层面完成
4.4 现代C++特性在策略模式中的应用
C++17/20的新特性可以让策略模式实现更优雅:
使用std::variant实现类型安全策略选择
cpp复制using StrategyVariant = variant<FastStrategy, SafeStrategy, FallbackStrategy>;
class VariantContext {
private:
StrategyVariant strategy;
public:
template<typename T>
void setStrategy(T&& s) {
strategy = forward<T>(s);
}
void execute() {
visit([](auto&& s) {
s.execute();
}, strategy);
}
};
使用concept约束策略类型
cpp复制template<typename T>
concept StrategyConcept = requires(T t) {
{ t.execute() } -> same_as<void>;
};
template<StrategyConcept T>
class ConceptContext {
private:
T strategy;
public:
void execute() {
strategy.execute();
}
};
使用lambda表达式作为轻量级策略
cpp复制class LambdaContext {
private:
function<void()> strategy;
public:
template<typename F>
void setStrategy(F&& f) {
strategy = forward<F>(f);
}
void execute() {
if(strategy) {
strategy();
}
}
};
// 使用示例
LambdaContext ctx;
ctx.setStrategy([]{
cout << "Custom lambda strategy" << endl;
});
ctx.execute();
在实际项目中,我发现策略模式最强大的地方不在于其实现多么精巧,而在于它迫使开发者将算法与业务逻辑分离。这种分离带来的可维护性提升,在项目规模扩大后会越发明显。特别是在需要频繁添加新算法或调整现有算法行为的场景下,策略模式可以显著降低修改成本。
