1. 策略模式基础回顾与进阶意义
在C++开发中,策略模式(Strategy Pattern)是我们最常用的设计模式之一。它定义了算法家族,分别封装起来,让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。
我见过太多项目因为早期没有采用策略模式,后期不得不进行痛苦的重构。比如一个游戏项目,最初只设计了简单难度,后来要增加中等和困难模式时,整个战斗系统几乎推倒重来。如果当初使用策略模式,只需要新增两个策略类即可。
策略模式的核心结构包含三个部分:
- Context:上下文角色,持有一个Strategy的引用
- Strategy:抽象策略角色,通常由接口或抽象类实现
- ConcreteStrategy:具体策略角色,包装了具体算法或行为
cpp复制// 经典策略模式示例
class Strategy {
public:
virtual void AlgorithmInterface() = 0;
};
class ConcreteStrategyA : public Strategy {
public:
void AlgorithmInterface() override {
cout << "算法A实现" << endl;
}
};
class Context {
private:
Strategy* strategy;
public:
Context(Strategy* s) : strategy(s) {}
void ContextInterface() {
strategy->AlgorithmInterface();
}
};
进阶策略模式的价值在于:
- 应对更复杂的业务场景
- 提升运行时策略切换的效率
- 实现策略的动态组合
- 优化策略与上下文的交互方式
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 策略模式的五种进阶实现技巧
2.1 策略工厂与自动注册
传统策略模式需要在客户端显式创建具体策略对象。我们可以引入工厂模式,结合自动注册机制,实现策略的自动发现和创建。
cpp复制// 策略工厂实现
class StrategyFactory {
private:
using Creator = std::function<std::unique_ptr<Strategy>()>;
std::unordered_map<std::string, Creator> creators_;
public:
static StrategyFactory& Instance() {
static StrategyFactory instance;
return instance;
}
void Register(const std::string& name, Creator creator) {
creators_[name] = creator;
}
std::unique_ptr<Strategy> Create(const std::string& name) {
auto it = creators_.find(name);
if (it != creators_.end()) {
return it->second();
}
return nullptr;
}
};
// 自动注册模板
template <typename T>
class AutoRegister {
public:
explicit AutoRegister(const std::string& name) {
StrategyFactory::Instance().Register(
name, []{ return std::make_unique<T>(); }
);
}
};
// 具体策略注册示例
class AdvancedStrategyA : public Strategy {
// 实现代码...
};
static AutoRegister<AdvancedStrategyA> regA("StrategyA");
这种实现方式的优势:
- 新增策略无需修改工厂代码
- 策略名称与实现解耦
- 支持运行时动态加载策略
注意:在多线程环境下使用时,需要确保工厂的线程安全性。可以使用std::call_once或mutex来保护注册过程。
2.2 策略组合模式
复杂业务场景下,单一策略往往不能满足需求。我们可以将多个策略组合使用,形成更强大的复合策略。
cpp复制class CompositeStrategy : public Strategy {
private:
std::vector<std::shared_ptr<Strategy>> strategies_;
public:
void AddStrategy(std::shared_ptr<Strategy> strategy) {
strategies_.push_back(strategy);
}
void AlgorithmInterface() override {
for (auto& strategy : strategies_) {
strategy->AlgorithmInterface();
}
}
};
// 使用示例
auto composite = std::make_shared<CompositeStrategy>();
composite->AddStrategy(std::make_shared<ConcreteStrategyA>());
composite->AddStrategy(std::make_shared<ConcreteStrategyB>());
Context ctx(composite.get());
ctx.ContextInterface();
实际项目中的经验:
- 组合顺序有时很重要,可以增加priority字段控制执行顺序
- 考虑添加短路逻辑,当某个策略满足条件时跳过后续策略
- 组合策略的性能开销需要评估,特别是在高频调用场景
2.3 基于策略的模板元编程
对于性能敏感的场景,我们可以使用模板将策略决策提前到编译期。这种方法完全消除了运行时多态的开销。
cpp复制template <typename TStrategy>
class ContextTemplate {
private:
TStrategy strategy;
public:
void ContextInterface() {
strategy.AlgorithmInterface();
}
};
// 策略实现作为模板参数
struct FastStrategy {
void AlgorithmInterface() {
// 编译期确定的实现
}
};
// 使用示例
ContextTemplate<FastStrategy> ctx;
ctx.ContextInterface();
适用场景分析:
- 策略在程序生命周期内不会改变
- 性能是关键考量因素
- 策略类型在编译期可知
我在一个高频交易系统中使用这种技术,性能提升了约15%。但调试难度也相应增加,需要权衡利弊。
2.4 策略与状态模式的结合
当策略需要根据上下文状态动态变化时,可以将策略模式与状态模式结合使用。
cpp复制class StateDependentContext {
private:
std::unique_ptr<State> currentState_;
public:
void ChangeState(std::unique_ptr<State> newState) {
currentState_ = std::move(newState);
}
void Request() {
currentState_->Handle(this);
}
};
class State {
public:
virtual void Handle(StateDependentContext* context) = 0;
virtual ~State() = default;
};
class ConcreteStateA : public State {
public:
void Handle(StateDependentContext* context) override {
// 使用策略A
context->ChangeState(std::make_unique<ConcreteStateB>());
}
};
这种模式特别适合游戏AI、工作流引擎等场景。我在一个电商订单系统中使用这种设计,优雅地处理了订单状态流转时的不同业务规则。
2.5 策略的惰性加载与缓存
对于创建成本高的策略,可以实现惰性加载和缓存机制。
cpp复制class HeavyStrategy : public Strategy {
// 初始化成本高的策略
};
class CachedStrategyProxy : public Strategy {
private:
std::unique_ptr<Strategy> realStrategy_;
std::mutex mtx_;
public:
void AlgorithmInterface() override {
std::lock_guard<std::mutex> lock(mtx_);
if (!realStrategy_) {
realStrategy_ = std::make_unique<HeavyStrategy>();
}
realStrategy_->AlgorithmInterface();
}
};
优化技巧:
- 使用双重检查锁定优化并发性能
- 考虑添加策略缓存池
- 对于无状态策略,可以共享同一个实例
3. 策略模式在游戏开发中的实战应用
3.1 游戏AI行为策略
在游戏开发中,不同的NPC可能需要不同的行为策略。我们可以为每种行为定义一个策略类。
cpp复制// 行为策略接口
class BehaviorStrategy {
public:
virtual void Update(NPC* npc) = 0;
virtual ~BehaviorStrategy() = default;
};
// 具体行为策略
class PatrolStrategy : public BehaviorStrategy {
public:
void Update(NPC* npc) override {
// 巡逻逻辑实现
}
};
class ChaseStrategy : public BehaviorStrategy {
public:
void Update(NPC* npc) override {
// 追逐玩家逻辑
}
};
// NPC类
class NPC {
private:
std::unique_ptr<BehaviorStrategy> behavior_;
public:
void SetBehavior(std::unique_ptr<BehaviorStrategy> behavior) {
behavior_ = std::move(behavior);
}
void Update() {
if (behavior_) {
behavior_->Update(this);
}
}
};
实际开发中的经验:
- 策略切换频率不宜过高,避免性能问题
- 考虑使用行为树组合简单策略
- 策略间共享数据可以通过黑板模式实现
3.2 游戏难度系统设计
不同游戏难度实际上是不同策略的实现。我们可以为每个难度级别定义一组策略。
cpp复制class DifficultyStrategy {
public:
virtual float GetEnemyHealthMultiplier() = 0;
virtual float GetPlayerDamageMultiplier() = 0;
// 其他难度相关参数...
};
class EasyDifficulty : public DifficultyStrategy {
float GetEnemyHealthMultiplier() override { return 0.7f; }
float GetPlayerDamageMultiplier() override { return 1.3f; }
};
class HardDifficulty : public DifficultyStrategy {
float GetEnemyHealthMultiplier() override { return 1.5f; }
float GetPlayerDamageMultiplier() override { return 0.8f; }
};
class GameManager {
private:
std::unique_ptr<DifficultyStrategy> difficulty_;
public:
void SetDifficulty(std::unique_ptr<DifficultyStrategy> diff) {
difficulty_ = std::move(diff);
}
void SpawnEnemy() {
float health = BASE_HEALTH * difficulty_->GetEnemyHealthMultiplier();
// 生成敌人...
}
};
提示:难度策略应该是无状态的,这样同一个策略实例可以被所有需要的地方共享。
4. 策略模式性能优化技巧
4.1 虚函数调用的性能影响
传统策略模式通过虚函数实现多态,会带来一定的性能开销。我们可以通过以下方式优化:
- 批量处理:一次性处理多个数据,分摊虚函数调用开销
- 策略内联:对于简单策略,使用模板策略模式
- 热点策略特殊处理:对高频调用的策略进行针对性优化
cpp复制// 批量处理示例
class BatchProcessingContext {
private:
Strategy* strategy_;
public:
void ProcessBatch(std::vector<Data>& data) {
for (auto& item : data) {
strategy_->Process(item);
}
}
};
4.2 内存访问优化
策略对象的内存布局会影响性能。考虑:
- 将频繁访问的数据放在缓存友好的结构中
- 避免策略对象过大导致缓存失效
- 对于小型策略,可以考虑就地构造
cpp复制// 小对象优化示例
template <typename Strategy>
class SmallStrategyHolder {
private:
std::aligned_storage_t<sizeof(Strategy), alignof(Strategy)> storage_;
bool initialized_ = false;
public:
template <typename... Args>
void Init(Args&&... args) {
new (&storage_) Strategy(std::forward<Args>(args)...);
initialized_ = true;
}
~SmallStrategyHolder() {
if (initialized_) {
reinterpret_cast<Strategy*>(&storage_)->~Strategy();
}
}
Strategy* Get() {
return initialized_ ? reinterpret_cast<Strategy*>(&storage_) : nullptr;
}
};
4.3 多线程环境下的策略模式
在多线程环境中使用策略模式需要注意:
- 策略对象的线程安全性
- 策略切换的原子性
- 避免虚假共享
cpp复制class ThreadSafeContext {
private:
std::shared_ptr<Strategy> strategy_;
mutable std::shared_mutex mtx_;
public:
void SetStrategy(std::shared_ptr<Strategy> newStrategy) {
std::unique_lock lock(mtx_);
strategy_ = std::move(newStrategy);
}
void Execute() {
std::shared_lock lock(mtx_);
if (strategy_) {
strategy_->AlgorithmInterface();
}
}
};
5. 策略模式常见问题与解决方案
5.1 策略膨胀问题
当策略类过多时,会导致代码难以维护。解决方案:
- 使用策略组合模式
- 引入策略层次结构
- 使用DSL定义简单策略
cpp复制// 策略层次结构示例
class BasicStrategy : public Strategy {
// 基础功能...
};
class AdvancedStrategy : public BasicStrategy {
// 扩展功能...
};
5.2 策略参数传递
当策略需要访问上下文大量数据时,参数传递变得复杂。解决方案:
- 将上下文引用传递给策略
- 使用参数对象封装
- 实现策略数据懒加载
cpp复制// 参数对象示例
struct StrategyParams {
int param1;
float param2;
std::string param3;
// ...
};
class ParametrizedStrategy : public Strategy {
public:
void AlgorithmInterface(const StrategyParams& params) {
// 使用参数...
}
};
5.3 策略与上下文的双向交互
有时策略需要回调上下文方法。可以通过以下方式实现:
- 将上下文接口传递给策略
- 使用观察者模式
- 采用依赖注入
cpp复制// 上下文回调接口
class IContextCallback {
public:
virtual void Notify(const std::string& event) = 0;
};
class CallbackStrategy : public Strategy {
private:
IContextCallback* callback_;
public:
CallbackStrategy(IContextCallback* cb) : callback_(cb) {}
void AlgorithmInterface() override {
// ...
if (callback_) {
callback_->Notify("Event occurred");
}
}
};
6. 现代C++中的策略模式演进
6.1 使用std::function作为策略
C++11引入的std::function可以作为轻量级策略接口。
cpp复制class FunctionStrategyContext {
private:
std::function<void()> strategy_;
public:
void SetStrategy(std::function<void()> strategy) {
strategy_ = std::move(strategy);
}
void Execute() {
if (strategy_) {
strategy_();
}
}
};
// 使用示例
FunctionStrategyContext ctx;
ctx.SetStrategy([] {
cout << "Lambda策略实现" << endl;
});
ctx.Execute();
6.2 策略模式与概念(Concepts)
C++20的概念(Concepts)可以更好地约束策略接口。
cpp复制template <typename T>
concept StrategyConcept = requires(T t) {
{ t.AlgorithmInterface() } -> std::same_as<void>;
};
template <StrategyConcept T>
class ConceptContext {
private:
T strategy_;
public:
void Execute() {
strategy_.AlgorithmInterface();
}
};
6.3 策略模式与协程
C++20协程可以与策略模式结合,实现异步策略。
cpp复制class AsyncStrategy {
public:
struct promise_type {
AsyncStrategy get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
virtual std::future<void> ExecuteAsync() = 0;
};
class ConcreteAsyncStrategy : public AsyncStrategy {
public:
std::future<void> ExecuteAsync() override {
co_await std::suspend_always{};
// 异步实现...
}
};
7. 策略模式测试与调试技巧
7.1 策略单元测试
为策略类编写有效的单元测试:
cpp复制// 使用Google Test示例
TEST(StrategyTest, ConcreteStrategyA_AlgorithmInterface) {
ConcreteStrategyA strategy;
testing::internal::CaptureStdout();
strategy.AlgorithmInterface();
std::string output = testing::internal::GetCapturedStdout();
EXPECT_EQ(output, "算法A实现\n");
}
// 模拟上下文测试
class MockContext : public IContextCallback {
public:
MOCK_METHOD(void, Notify, (const std::string&), (override));
};
TEST(StrategyTest, CallbackStrategy_NotifiesContext) {
MockContext mock;
EXPECT_CALL(mock, Notify("Event occurred")).Times(1);
CallbackStrategy strategy(&mock);
strategy.AlgorithmInterface();
}
7.2 策略调试技巧
调试策略模式的实用技巧:
- 为策略添加唯一标识符,便于日志追踪
- 实现策略的ToString方法,方便调试时查看当前策略
- 使用装饰器模式添加调试逻辑
cpp复制// 调试装饰器示例
class DebugStrategy : public Strategy {
private:
Strategy* wrapped_;
std::string name_;
public:
DebugStrategy(Strategy* wrapped, std::string name)
: wrapped_(wrapped), name_(std::move(name)) {}
void AlgorithmInterface() override {
std::cout << "Entering strategy: " << name_ << std::endl;
auto start = std::chrono::high_resolution_clock::now();
wrapped_->AlgorithmInterface();
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
std::cout << "Exiting strategy: " << name_
<< ", took " << elapsed.count() << "s" << std::endl;
}
};
7.3 策略性能分析
分析策略模式性能的方法:
- 使用微基准测试比较不同策略
- 分析虚函数调用的开销
- 检查策略对象的内存访问模式
cpp复制// 微基准测试示例
static void BM_StrategyPattern(benchmark::State& state) {
Context ctx(std::make_unique<ConcreteStrategyA>());
for (auto _ : state) {
ctx.ContextInterface();
}
}
BENCHMARK(BM_StrategyPattern);
8. 策略模式在大型项目中的架构设计
8.1 策略模块化设计
在大型项目中,策略实现应该模块化:
- 每个策略放在独立模块/库中
- 使用接口隔离原则
- 明确策略依赖关系
text复制// 项目结构示例
project/
├── core/ # 核心模块
├── strategies/
│ ├── strategy_a/ # 策略A实现
│ ├── strategy_b/ # 策略B实现
│ └── shared/ # 策略共享代码
└── app/ # 主应用程序
8.2 策略版本管理
当策略需要演进时,考虑版本兼容性:
- 为策略接口添加版本号
- 实现策略适配器处理不同版本
- 提供策略迁移工具
cpp复制// 版本化策略接口
class StrategyV1 {
public:
virtual void AlgorithmInterfaceV1() = 0;
};
class StrategyV2 {
public:
virtual void AlgorithmInterfaceV2() = 0;
};
// 适配器
class V1ToV2Adapter : public StrategyV2 {
private:
StrategyV1* v1Strategy_;
public:
V1ToV2Adapter(StrategyV1* v1) : v1Strategy_(v1) {}
void AlgorithmInterfaceV2() override {
v1Strategy_->AlgorithmInterfaceV1();
// 额外的V2逻辑...
}
};
8.3 策略配置化
将策略选择逻辑外部化,通过配置文件决定使用哪种策略:
cpp复制// 配置驱动策略选择
std::unique_ptr<Strategy> CreateStrategyFromConfig(const Config& config) {
auto type = config.GetStrategyType();
if (type == "A") {
return std::make_unique<ConcreteStrategyA>();
} else if (type == "B") {
return std::make_unique<ConcreteStrategyB>();
}
return nullptr;
}
在实际项目中,我通常会结合JSON或YAML配置文件,实现策略的动态选择和参数配置。
