1. 策略模式基础与C++实现要点
策略模式作为行为型设计模式的经典代表,其核心思想是将算法族进行封装,使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端,完美符合开闭原则。在C++中实现策略模式时,我们需要特别注意语言特性带来的实现差异。
1.1 经典策略模式结构
标准策略模式通常包含三个关键组件:
- Context(上下文):维护对具体策略的引用,通过策略接口与策略对象交互
- Strategy(策略接口):声明所有具体策略必须实现的算法接口
- ConcreteStrategy(具体策略):实现策略接口的具体算法类
C++实现示例:
cpp复制class SortingStrategy {
public:
virtual void sort(vector<int>& data) = 0;
virtual ~SortingStrategy() = default;
};
class QuickSort : public SortingStrategy {
public:
void sort(vector<int>& data) override {
// 快速排序实现
cout << "Sorting using QuickSort" << endl;
}
};
class MergeSort : public SortingStrategy {
public:
void sort(vector<int>& data) override {
// 归并排序实现
cout << "Sorting using MergeSort" << endl;
}
};
class Sorter {
unique_ptr<SortingStrategy> strategy;
public:
explicit Sorter(unique_ptr<SortingStrategy>&& s)
: strategy(std::move(s)) {}
void setStrategy(unique_ptr<SortingStrategy>&& s) {
strategy = std::move(s);
}
void execute(vector<int>& data) {
strategy->sort(data);
}
};
1.2 C++实现特性考量
在C++中实现策略模式时,有几个关键点需要特别注意:
-
对象生命周期管理:
- 优先使用智能指针(unique_ptr/shared_ptr)管理策略对象
- 考虑策略对象是否需要有独立于Context的生命周期
-
性能优化:
- 对于小型策略对象,考虑使用值语义而非指针
- 高频调用的策略方法可声明为inline
-
接口设计:
- 策略接口应尽可能精简,避免虚函数过多导致性能下降
- 考虑使用非虚接口(NVI)模式增强扩展性
提示:在性能敏感场景中,策略对象的创建开销可能成为瓶颈,此时可以考虑对象池模式进行优化。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C++策略模式常见变体实现
在实际工程实践中,经典的策略模式往往需要根据具体场景进行调整和扩展。以下是几种在C++中广泛使用的策略模式变体。
2.1 基于模板的策略模式
模板策略模式通过编译期多态替代运行时多态,完全消除虚函数开销:
cpp复制template<typename Strategy>
class Sorter {
Strategy strategy;
public:
void execute(vector<int>& data) {
strategy.sort(data);
}
};
struct QuickSortPolicy {
void sort(vector<int>& data) {
// 快速排序实现
}
};
struct MergeSortPolicy {
void sort(vector<int>& data) {
// 归并排序实现
}
};
// 使用示例
Sorter<QuickSortPolicy> quickSorter;
vector<int> data = {...};
quickSorter.execute(data);
适用场景:
- 策略类型在编译期已知
- 需要极致性能的场景
- 策略不需要运行时动态切换
2.2 基于函数对象的策略模式
利用C++的函数对象特性,可以创建更灵活的策略实现:
cpp复制class Sorter {
function<void(vector<int>&)> strategy;
public:
explicit Sorter(function<void(vector<int>&)>&& s)
: strategy(std::move(s)) {}
void setStrategy(function<void(vector<int>&)>&& s) {
strategy = std::move(s);
}
void execute(vector<int>& data) {
strategy(data);
}
};
// 使用示例
auto quickSort = [](vector<int>& data) {
// lambda实现快速排序
};
Sorter sorter(quickSort);
sorter.execute(data);
优势分析:
- 支持lambda表达式,代码更简洁
- 兼容普通函数、函数对象、成员函数等多种可调用对象
- 运行时绑定灵活度高
2.3 策略模式与类型擦除结合
类型擦除技术可以在保持接口统一性的同时,支持不同类型的策略实现:
cpp复制class SortConcept {
public:
virtual void sort(vector<int>&) = 0;
virtual ~SortConcept() = default;
};
template<typename T>
class SortModel : public SortConcept {
T impl;
public:
SortModel(T&& t) : impl(std::forward<T>(t)) {}
void sort(vector<int>& data) override { impl.sort(data); }
};
class Sorter {
unique_ptr<SortConcept> strategy;
public:
template<typename T>
Sorter(T&& t)
: strategy(make_unique<SortModel<T>>(std::forward<T>(t))) {}
void execute(vector<int>& data) {
strategy->sort(data);
}
};
技术要点:
- 通过Concept-Model模式实现类型安全擦除
- 既保持了运行时多态的灵活性
- 又支持值语义策略对象的高效传递
3. 高级应用与性能优化
3.1 策略模式的线程安全实现
在多线程环境下使用策略模式需要特别注意线程安全问题:
cpp复制class ThreadSafeSorter {
mutex mtx;
unique_ptr<SortingStrategy> strategy;
public:
void setStrategy(unique_ptr<SortingStrategy>&& s) {
lock_guard<mutex> lock(mtx);
strategy = std::move(s);
}
void execute(vector<int>& data) {
lock_guard<mutex> lock(mtx);
if(strategy) strategy->sort(data);
}
};
注意事项:
- 策略切换和执行需要同步
- 避免在策略执行过程中修改策略
- 考虑使用读写锁优化读多写少场景
3.2 策略对象复用与缓存
频繁创建策略对象可能导致性能问题,可以通过对象池优化:
cpp复制class StrategyPool {
unordered_map<type_index, shared_ptr<SortingStrategy>> pool;
mutex mtx;
public:
template<typename T>
shared_ptr<T> getStrategy() {
lock_guard<mutex> lock(mtx);
auto it = pool.find(typeid(T));
if(it == pool.end()) {
auto ptr = make_shared<T>();
pool[typeid(T)] = ptr;
return ptr;
}
return dynamic_pointer_cast<T>(it->second);
}
};
优化效果:
- 相同类型策略对象只创建一次
- 通过shared_ptr实现自动生命周期管理
- 减少内存分配开销
3.3 策略模式与SIMD优化
对于计算密集型策略,可以利用SIMD指令集进行优化:
cpp复制class SIMDSort : public SortingStrategy {
public:
void sort(vector<int>& data) override {
#ifdef __AVX2__
// AVX2指令集优化实现
#else
// 普通实现
#endif
}
};
实现技巧:
- 通过CPU特性检测选择最优策略
- 使用指令集内联函数优化关键路径
- 提供多版本实现供运行时选择
4. 工程实践中的问题与解决方案
4.1 策略膨胀问题
当策略类过多时,会导致代码难以维护。解决方案:
- 策略组合模式:
cpp复制class CompositeStrategy : public SortingStrategy {
vector<unique_ptr<SortingStrategy>> strategies;
public:
void addStrategy(unique_ptr<SortingStrategy>&& s) {
strategies.push_back(std::move(s));
}
void sort(vector<int>& data) override {
for(auto& s : strategies) {
s->sort(data);
}
}
};
- 策略工厂方法:
cpp复制class StrategyFactory {
public:
static unique_ptr<SortingStrategy> create(const string& name) {
if(name == "quick") return make_unique<QuickSort>();
if(name == "merge") return make_unique<MergeSort>();
throw invalid_argument("Unknown strategy");
}
};
4.2 策略配置化
将策略选择逻辑外置到配置文件中:
json复制{
"sorting_strategy": {
"type": "quick",
"params": {
"threshold": 1000
}
}
}
对应的策略加载代码:
cpp复制unique_ptr<SortingStrategy> loadStrategy(const json& config) {
auto type = config["type"].get<string>();
if(type == "quick") {
auto threshold = config["params"]["threshold"].get<int>();
return make_unique<QuickSort>(threshold);
}
// 其他策略...
}
4.3 策略模式与其它模式的结合
- 策略+装饰器模式:
cpp复制class LoggingStrategy : public SortingStrategy {
unique_ptr<SortingStrategy> wrapped;
public:
explicit LoggingStrategy(unique_ptr<SortingStrategy>&& s)
: wrapped(std::move(s)) {}
void sort(vector<int>& data) override {
auto start = chrono::high_resolution_clock::now();
wrapped->sort(data);
auto end = chrono::high_resolution_clock::now();
cout << "Sorting took "
<< chrono::duration_cast<chrono::milliseconds>(end-start).count()
<< "ms" << endl;
}
};
- 策略+观察者模式:
cpp复制class ObservableStrategy : public SortingStrategy {
vector<function<void(int)>> progressListeners;
protected:
void notifyProgress(int percent) {
for(auto& listener : progressListeners) {
listener(percent);
}
}
public:
void addProgressListener(function<void(int)>&& listener) {
progressListeners.push_back(std::move(listener));
}
};
class ObservableQuickSort : public ObservableStrategy {
public:
void sort(vector<int>& data) override {
// 排序过程中调用notifyProgress
}
};
在实际项目中,我经常发现策略模式的最佳实践是:从简单实现开始,随着需求变化逐步演进到更复杂的变体。过早优化往往会导致设计过度复杂,而适度的演进则能保持代码的灵活性和可维护性。
