1. 策略模式的核心思想与适用场景
策略模式(Strategy Pattern)是行为型设计模式中最常用的一种,它定义了算法族,分别封装起来,让它们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。
在C++中实现策略模式时,我们通常会遇到以下几个典型场景:
- 当存在多种相似算法,且需要在运行时动态切换时
- 当算法中包含大量条件判断语句时(if-else或switch-case)
- 当需要隔离算法实现细节与使用场景时
- 当算法需要频繁变更或扩展时
1.1 基础实现方式
最基本的策略模式实现包含三个核心组件:
cpp复制// 策略接口
class Strategy {
public:
virtual ~Strategy() = default;
virtual void execute() const = 0;
};
// 具体策略A
class ConcreteStrategyA : public Strategy {
public:
void execute() const override {
std::cout << "Executing strategy A\n";
}
};
// 上下文类
class Context {
private:
std::unique_ptr<Strategy> strategy_;
public:
explicit Context(std::unique_ptr<Strategy> strategy)
: strategy_(std::move(strategy)) {}
void setStrategy(std::unique_ptr<Strategy> strategy) {
strategy_ = std::move(strategy);
}
void executeStrategy() const {
strategy_->execute();
}
};
这种实现方式虽然简单,但在实际工程中会遇到诸多问题,比如策略对象的生命周期管理、线程安全性、性能开销等。
2. 现代C++中的策略模式优化
现代C++(C++11及以上)提供了多种工具可以优化传统策略模式的实现。
2.1 使用std::function替代虚函数
虚函数调用有一定的性能开销,我们可以使用std::function和lambda表达式来实现无虚函数的策略模式:
cpp复制class FunctionStrategyContext {
private:
std::function<void()> strategy_;
public:
template<typename T>
explicit FunctionStrategyContext(T&& strategy)
: strategy_(std::forward<T>(strategy)) {}
template<typename T>
void setStrategy(T&& strategy) {
strategy_ = std::forward<T>(strategy);
}
void executeStrategy() const {
strategy_();
}
};
// 使用示例
FunctionStrategyContext ctx([](){
std::cout << "Lambda strategy\n";
});
这种方式消除了虚函数调用的开销,同时保持了策略模式的灵活性。
2.2 策略对象的生命周期管理
在复杂系统中,策略对象可能需要访问外部资源或保持状态。现代C++提供了多种智能指针来管理策略对象的生命周期:
cpp复制class ResourceIntensiveStrategy : public Strategy {
// 可能持有文件句柄、网络连接等资源
public:
void execute() const override {
// 使用资源的操作
}
};
void useStrategy() {
auto strategy = std::make_shared<ResourceIntensiveStrategy>();
Context ctx(strategy);
// 当ctx和所有strategy的shared_ptr都销毁时,资源会自动释放
}
3. 策略模式的高级应用技巧
3.1 策略组合模式
有时我们需要将多个策略组合使用。可以通过组合模式来实现策略的组合:
cpp复制class CompositeStrategy : public Strategy {
std::vector<std::shared_ptr<Strategy>> strategies_;
public:
void addStrategy(std::shared_ptr<Strategy> strategy) {
strategies_.push_back(strategy);
}
void execute() const override {
for (const auto& s : strategies_) {
s->execute();
}
}
};
3.2 策略工厂模式
当策略对象的创建逻辑复杂时,可以结合工厂模式:
cpp复制class StrategyFactory {
public:
static std::unique_ptr<Strategy> createStrategy(const std::string& type) {
if (type == "A") return std::make_unique<ConcreteStrategyA>();
if (type == "B") return std::make_unique<ConcreteStrategyB>();
throw std::runtime_error("Unknown strategy type");
}
};
4. 性能优化与线程安全考虑
4.1 策略对象的复用
频繁创建销毁策略对象可能导致性能问题。可以考虑使用对象池模式:
cpp复制class StrategyPool {
std::unordered_map<std::string, std::shared_ptr<Strategy>> pool_;
public:
std::shared_ptr<Strategy> getStrategy(const std::string& type) {
auto it = pool_.find(type);
if (it != pool_.end()) return it->second;
auto strategy = StrategyFactory::createStrategy(type);
pool_[type] = strategy;
return strategy;
}
};
4.2 线程安全实现
如果策略对象会被多线程访问,需要考虑线程安全性:
cpp复制class ThreadSafeContext {
std::mutex mtx_;
std::unique_ptr<Strategy> strategy_;
public:
void setStrategy(std::unique_ptr<Strategy> strategy) {
std::lock_guard<std::mutex> lock(mtx_);
strategy_ = std::move(strategy);
}
void executeStrategy() const {
std::lock_guard<std::mutex> lock(mtx_);
if (strategy_) strategy_->execute();
}
};
5. 实际工程中的策略模式应用
5.1 排序算法选择器
一个典型的应用场景是实现可配置的排序算法:
cpp复制class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) const = 0;
};
class QuickSort : public SortStrategy {
void sort(std::vector<int>& data) const override {
// 快速排序实现
}
};
class MergeSort : public SortStrategy {
void sort(std::vector<int>& data) const override {
// 归并排序实现
}
};
class Sorter {
std::unique_ptr<SortStrategy> strategy_;
public:
void setStrategy(std::unique_ptr<SortStrategy> strategy) {
strategy_ = std::move(strategy);
}
void sortData(std::vector<int>& data) {
strategy_->sort(data);
}
};
5.2 游戏AI行为策略
在游戏开发中,策略模式常用于实现不同的AI行为:
cpp复制class AIBehavior {
public:
virtual ~AIBehavior() = default;
virtual void update(GameObject& obj) = 0;
};
class AggressiveBehavior : public AIBehavior {
void update(GameObject& obj) override {
// 攻击性行为逻辑
}
};
class DefensiveBehavior : public AIBehavior {
void update(GameObject& obj) override {
// 防御性行为逻辑
}
};
class NPC {
std::unique_ptr<AIBehavior> behavior_;
public:
void setBehavior(std::unique_ptr<AIBehavior> behavior) {
behavior_ = std::move(behavior);
}
void update() {
behavior_->update(*this);
}
};
6. 策略模式的测试与调试
6.1 单元测试策略
为策略模式编写测试时,可以使用模拟对象:
cpp复制class MockStrategy : public Strategy {
public:
MOCK_METHOD(void, execute, (), (const, override));
};
TEST(StrategyTest, ContextExecutesStrategy) {
auto mockStrategy = std::make_unique<MockStrategy>();
EXPECT_CALL(*mockStrategy, execute()).Times(1);
Context ctx(std::move(mockStrategy));
ctx.executeStrategy();
}
6.2 调试技巧
调试策略模式时,可以考虑以下方法:
- 为策略接口添加日志功能
- 使用装饰器模式包装策略对象以添加调试信息
- 在上下文类中添加策略执行前后的钩子函数
cpp复制class LoggingStrategyDecorator : public Strategy {
std::shared_ptr<Strategy> wrapped_;
public:
explicit LoggingStrategyDecorator(std::shared_ptr<Strategy> wrapped)
: wrapped_(wrapped) {}
void execute() const override {
std::cout << "Before strategy execution\n";
wrapped_->execute();
std::cout << "After strategy execution\n";
}
};
7. 策略模式与其他设计模式的结合
7.1 策略模式与模板方法模式
策略模式可以与模板方法模式结合,提供更灵活的行为定制:
cpp复制class TemplateStrategy {
public:
void execute() const {
beforeHook();
doExecute();
afterHook();
}
protected:
virtual void doExecute() const = 0;
private:
void beforeHook() const {
// 公共前置处理
}
void afterHook() const {
// 公共后置处理
}
};
7.2 策略模式与观察者模式
策略的执行结果可能需要通知其他对象:
cpp复制class ObservableStrategy : public Strategy {
std::vector<std::function<void()>> observers_;
public:
void addObserver(std::function<void()> observer) {
observers_.push_back(observer);
}
void execute() const override {
// 策略逻辑
notifyObservers();
}
private:
void notifyObservers() const {
for (const auto& o : observers_) {
o();
}
}
};
8. 策略模式在大型项目中的最佳实践
8.1 策略注册机制
在大型项目中,可以实现策略的自动注册机制:
cpp复制class StrategyRegistry {
static std::map<std::string, std::function<std::unique_ptr<Strategy>()>> registry_;
public:
static void registerStrategy(const std::string& name,
std::function<std::unique_ptr<Strategy>()> creator) {
registry_[name] = creator;
}
static std::unique_ptr<Strategy> create(const std::string& name) {
auto it = registry_.find(name);
if (it == registry_.end()) return nullptr;
return it->second();
}
};
// 策略自动注册宏
#define REGISTER_STRATEGY(NAME, TYPE) \
namespace { \
struct TYPE##Register { \
TYPE##Register() { \
StrategyRegistry::registerStrategy(NAME, []() { \
return std::make_unique<TYPE>(); \
}); \
} \
}; \
TYPE##Register TYPE##_register; \
}
8.2 策略的配置化
通过配置文件动态加载策略:
cpp复制class ConfigurableContext {
std::unique_ptr<Strategy> strategy_;
public:
void loadFromConfig(const Config& config) {
auto strategyName = config.get("strategy");
strategy_ = StrategyRegistry::create(strategyName);
}
void execute() {
if (strategy_) strategy_->execute();
}
};
9. 策略模式的性能考量
9.1 虚函数调用开销分析
虚函数调用通常比普通函数调用慢,因为:
- 需要通过虚函数表间接调用
- 阻碍了编译器的内联优化
- 可能导致缓存不命中
在性能敏感的场景,可以考虑:
- 使用std::function替代虚函数
- 使用CRTP模式实现静态多态
- 将策略实现为模板参数
9.2 内存访问模式优化
策略对象的内存布局会影响性能:
- 尽量使策略对象小巧
- 避免策略对象内部指针跳转
- 考虑使用连续内存存储策略数据
cpp复制template<typename Strategy>
class ContextWithStaticStrategy {
Strategy strategy_;
public:
void execute() {
strategy_.execute();
}
};
10. C++20/23中的新特性应用
10.1 使用概念约束策略类型
C++20的概念可以更好地约束策略类型:
cpp复制template<typename S>
concept StrategyConcept = requires(S s) {
{ s.execute() } -> std::same_as<void>;
};
template<StrategyConcept S>
class ConceptContext {
S strategy_;
public:
void execute() {
strategy_.execute();
}
};
10.2 使用协程实现异步策略
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() { std::terminate(); }
};
std::future<void> executeAsync() {
co_return;
}
};
在实际项目中,策略模式的选择和实现需要根据具体需求权衡。对于简单的策略,使用std::function可能更合适;对于复杂的策略继承体系,传统的虚函数方式可能更清晰。关键是要保持接口的稳定性和实现的灵活性。
