1. 策略模式核心概念解析
策略模式是行为型设计模式中最实用的模式之一,它通过将算法族封装成独立的类,使它们能够相互替换。这种模式让算法的变化独立于使用算法的客户端,完美体现了"开闭原则"。
在C++中实现策略模式时,通常会看到以下核心组件:
- Strategy接口:定义所有支持的算法族的公共接口
- ConcreteStrategy:实现Strategy接口的具体算法类
- Context:持有一个Strategy对象的引用,通过委托调用具体算法
关键理解:策略模式不是简单地封装算法,而是建立了一套可插拔的算法交换机制。这种机制在运行时通过多态实现动态切换,而不是通过条件判断语句硬编码。
2. C++实现策略模式的三种经典方式
2.1 基于继承的标准实现
这是最教科书式的实现方式,直接对应设计模式原教旨主义的实现:
class SortingStrategy { public: virtual ~SortingStrategy() = default; virtual void sort(vector<int>& data) = 0; }; class QuickSort : public SortingStrategy { public: void sort(vector<int>& data) override { cout << "Using quick sort" << endl; // 快速排序实现 } }; class MergeSort : public SortingStrategy { public: void sort(vector<int>& data) override { cout << "Using merge sort" << endl; // 归并排序实现 } }; class Sorter { unique_ptr<SortingStrategy> strategy; public: explicit Sorter(unique_ptr<SortingStrategy>&& s) : strategy(move(s)) {} void setStrategy(unique_ptr<SortingStrategy>&& s) { strategy = move(s); } void executeSort(vector<int>& data) { if(strategy) { strategy->sort(data); } } };2.2 基于函数指针的轻量实现
对于简单场景,可以使用函数指针避免虚函数开销:
using SortFunc = void(*)(vector<int>&); void quickSort(vector<int>& data) { // 快速排序实现 } void mergeSort(vector<int>& data) { // 归并排序实现 } class Sorter { SortFunc strategy = nullptr; public: explicit Sorter(SortFunc f) : strategy(f) {} void setStrategy(SortFunc f) { strategy = f; } void executeSort(vector<int>& data) { if(strategy) { strategy(data); } } };2.3 基于std::function的现代实现
C++11之后更推荐使用std::function,它比函数指针更灵活:
class Sorter { function<void(vector<int>&)> strategy; public: explicit Sorter(function<void(vector<int>&)> f) : strategy(f) {} void setStrategy(function<void(vector<int>&)> f) { strategy = f; } void executeSort(vector<int>& data) { if(strategy) { strategy(data); } } }; // 使用lambda表达式 Sorter sorter([](vector<int>& data) { // 自定义排序逻辑 });3. 策略模式的五大实战应用场景
3.1 算法选择器
最常见的应用场景是运行时算法选择。比如图形渲染中根据硬件能力选择不同的渲染策略:
class RenderStrategy { public: virtual void render(const Scene& scene) = 0; }; class DirectXRenderer : public RenderStrategy { // DX实现 }; class OpenGLRenderer : public RenderStrategy { // OpenGL实现 }; class VulkanRenderer : public RenderStrategy { // Vulkan实现 }; class GraphicsEngine { unique_ptr<RenderStrategy> renderer; public: void setRenderer(unique_ptr<RenderStrategy>&& r) { renderer = move(r); } void renderFrame(const Scene& scene) { renderer->render(scene); } };3.2 业务规则引擎
电商系统中的折扣策略是经典案例:
class DiscountStrategy { public: virtual double applyDiscount(double price) = 0; }; class RegularDiscount : public DiscountStrategy { double applyDiscount(double price) override { return price * 0.9; } }; class VIPDiscount : public DiscountStrategy { double applyDiscount(double price) override { return price * 0.7; } }; class Order { unique_ptr<DiscountStrategy> discount; double total; public: void setDiscount(unique_ptr<DiscountStrategy>&& d) { discount = move(d); } double checkout() { return discount ? discount->applyDiscount(total) : total; } };3.3 游戏AI行为
游戏NPC的不同行为模式:
class AIBehavior { public: virtual void update(NPC& npc) = 0; }; class AggressiveBehavior : public AIBehavior { void update(NPC& npc) override { // 攻击玩家逻辑 } }; class DefensiveBehavior : public AIBehavior { void update(NPC& npc) override { // 防御逻辑 } }; class NPCharacter { unique_ptr<AIBehavior> behavior; public: void setBehavior(unique_ptr<AIBehavior>&& b) { behavior = move(b); } void update() { behavior->update(*this); } };3.4 数据序列化
支持多种格式的数据导出:
class Serializer { public: virtual string serialize(const Data& data) = 0; }; class JSONSerializer : public Serializer { string serialize(const Data& data) override { // JSON序列化实现 } }; class XMLSerializer : public Serializer { string serialize(const Data& data) override { // XML序列化实现 } }; class DataExporter { unique_ptr<Serializer> serializer; public: void setSerializer(unique_ptr<Serializer>&& s) { serializer = move(s); } string exportData(const Data& data) { return serializer->serialize(data); } };3.5 单元测试模拟
测试中使用策略模式注入模拟对象:
class Database { public: virtual User getUser(int id) = 0; }; class RealDatabase : public Database { User getUser(int id) override { // 真实数据库查询 } }; class MockDatabase : public Database { User getUser(int id) override { return User{id, "Test User"}; } }; class UserService { unique_ptr<Database> db; public: explicit UserService(unique_ptr<Database>&& d) : db(move(d)) {} User getUser(int id) { return db->getUser(id); } };4. 策略模式的高级应用技巧
4.1 策略组合模式
多个策略可以组合使用形成更复杂的行为:
class LogStrategy { public: virtual void log(const string& message) = 0; }; class ConsoleLogger : public LogStrategy { void log(const string& msg) override { cout << msg << endl; } }; class FileLogger : public LogStrategy { void log(const string& msg) override { ofstream file("log.txt", ios::app); file << msg << endl; } }; class CompositeLogger : public LogStrategy { vector<unique_ptr<LogStrategy>> loggers; public: void addLogger(unique_ptr<LogStrategy>&& logger) { loggers.push_back(move(logger)); } void log(const string& msg) override { for(auto& l : loggers) { l->log(msg); } } };4.2 策略工厂模式
结合工厂模式创建策略对象:
class StrategyFactory { public: static unique_ptr<SortingStrategy> create(const string& type) { if(type == "quick") { return make_unique<QuickSort>(); } if(type == "merge") { return make_unique<MergeSort>(); } throw invalid_argument("Unknown strategy type"); } }; // 使用方式 auto sorter = Sorter(StrategyFactory::create("quick"));4.3 策略缓存机制
对昂贵策略对象实施缓存:
class StrategyCache { unordered_map<string, unique_ptr<SortingStrategy>> cache; public: SortingStrategy* get(const string& type) { auto it = cache.find(type); if(it == cache.end()) { it = cache.emplace(type, StrategyFactory::create(type)).first; } return it->second.get(); } };4.4 策略参数化配置
通过配置文件动态加载策略:
{ "sorting_strategy": "quick", "rendering_strategy": "vulkan" }class ConfigLoader { public: static unique_ptr<SortingStrategy> loadSortingStrategy() { auto config = loadConfigFile(); return StrategyFactory::create(config["sorting_strategy"]); } };5. 策略模式的性能优化
5.1 避免虚函数开销
对于性能敏感场景,可以使用CRTP模式:
template<typename T> class SortingStrategy { public: void sort(vector<int>& data) { static_cast<T*>(this)->doSort(data); } }; class QuickSort : public SortingStrategy<QuickSort> { public: void doSort(vector<int>& data) { // 快速排序实现 } }; template<typename Strategy> class Sorter { Strategy strategy; public: void executeSort(vector<int>& data) { strategy.sort(data); } };5.2 策略对象池
复用策略对象减少内存分配:
class StrategyPool { vector<unique_ptr<SortingStrategy>> pool; public: SortingStrategy* acquire() { if(pool.empty()) { return new QuickSort(); } auto ptr = move(pool.back()); pool.pop_back(); return ptr.release(); } void release(unique_ptr<SortingStrategy>&& s) { pool.push_back(move(s)); } };5.3 编译时策略选择
使用模板在编译期确定策略:
template<typename Strategy> class Sorter { Strategy strategy; public: void executeSort(vector<int>& data) { strategy.sort(data); } }; // 使用方式 Sorter<QuickSort> sorter; sorter.executeSort(data);6. 策略模式的最佳实践
6.1 何时使用策略模式
策略模式特别适用于以下场景:
- 一个系统需要在多种算法中选择一种
- 需要避免使用多重条件转移语句
- 算法需要自由切换的场景
- 算法实现可能被其他系统复用
6.2 策略模式的优势
- 符合开闭原则:新增策略无需修改现有代码
- 避免条件语句:用多态代替条件判断
- 算法复用:策略对象可以在不同上下文中复用
- 运行时灵活性:策略可以在运行时动态切换
6.3 策略模式的局限性
- 客户端必须了解不同策略的区别
- 策略对象会增加系统对象数量
- 通信开销:策略与上下文可能需要交换数据
经验之谈:当策略超过5个时,考虑引入工厂模式或配置机制来管理策略对象的创建。
7. 策略模式与其他模式的关系
7.1 策略模式 vs 状态模式
两者类图相似但意图不同:
- 策略模式:客户端主动选择算法
- 状态模式:状态转换由内部条件触发
7.2 策略模式 vs 模板方法模式
都是封装算法,但:
- 策略模式:对象组合,运行时绑定
- 模板方法:类继承,编译时确定
7.3 策略模式 vs 命令模式
命令模式强调动作的执行和撤销,而策略模式专注于算法的替换。
8. 实际项目中的策略模式应用
8.1 跨平台开发中的策略应用
处理不同平台的系统API差异:
class FileDialogStrategy { public: virtual string openFile() = 0; }; class WindowsFileDialog : public FileDialogStrategy { string openFile() override { // Windows系统API调用 } }; class MacFileDialog : public FileDialogStrategy { string openFile() override { // Mac系统API调用 } }; class FileDialog { unique_ptr<FileDialogStrategy> strategy; public: FileDialog() { #ifdef _WIN32 strategy = make_unique<WindowsFileDialog>(); #elif __APPLE__ strategy = make_unique<MacFileDialog>(); #endif } string open() { return strategy->openFile(); } };8.2 网络通信中的策略模式
处理不同的网络协议:
class ProtocolStrategy { public: virtual void send(const Packet& packet) = 0; }; class TCPProtocol : public ProtocolStrategy { void send(const Packet& packet) override { // TCP实现 } }; class UDPProtocol : public ProtocolStrategy { void send(const Packet& packet) override { // UDP实现 } }; class NetworkManager { unique_ptr<ProtocolStrategy> protocol; public: void setProtocol(unique_ptr<ProtocolStrategy>&& p) { protocol = move(p); } void sendData(const Packet& packet) { protocol->send(packet); } };8.3 数据库访问策略
支持多种数据库后端:
class DatabaseStrategy { public: virtual vector<Record> query(const string& sql) = 0; }; class MySQLStrategy : public DatabaseStrategy { vector<Record> query(const string& sql) override { // MySQL查询实现 } }; class PostgreSQLStrategy : public DatabaseStrategy { vector<Record> query(const string& sql) override { // PostgreSQL查询实现 } }; class DAL { unique_ptr<DatabaseStrategy> db; public: void setDatabase(unique_ptr<DatabaseStrategy>&& d) { db = move(d); } vector<Record> executeQuery(const string& sql) { return db->query(sql); } };9. 策略模式的测试策略
9.1 单元测试策略实现
测试具体策略实现:
TEST(QuickSortTest, SortsNumbersCorrectly) { vector<int> data = {3,1,4,2}; QuickSort strategy; strategy.sort(data); ASSERT_EQ(vector<int>{1,2,3,4}, data); }9.2 模拟策略测试上下文
使用模拟对象测试上下文:
class MockStrategy : public SortingStrategy { public: MOCK_METHOD(void, sort, (vector<int>&), (override)); }; TEST(SorterTest, DelegatesToStrategy) { MockStrategy mock; vector<int> testData; EXPECT_CALL(mock, sort(_)).Times(1); Sorter sorter(make_unique<MockStrategy>(mock)); sorter.executeSort(testData); }9.3 策略切换测试
验证策略切换行为:
TEST(SorterTest, SwitchesStrategies) { vector<int> data; auto quick = make_unique<QuickSort>(); auto merge = make_unique<MergeSort>(); Sorter sorter(move(quick)); sorter.setStrategy(move(merge)); sorter.executeSort(data); // 验证使用归并排序 }10. 策略模式的常见误用与规避
10.1 策略膨胀问题
当策略类过多时会导致系统复杂化。解决方案:
- 使用策略工厂统一管理
- 合并相似策略
- 引入策略层次结构
10.2 上下文过载问题
上下文类承担太多与策略无关的职责。应该:
- 保持上下文精简
- 将与策略无关的逻辑移到其他类
- 遵循单一职责原则
10.3 策略通信问题
策略之间需要通信时:
- 通过上下文共享状态
- 使用中介者模式
- 避免策略直接相互依赖
10.4 过度设计陷阱
不是所有算法变化都需要策略模式:
- 简单条件判断足够时不要用策略
- 算法很少变化时可能不需要
- 权衡模式引入的复杂度
实际经验:在大型长期维护的项目中,即使当前只有两种算法,如果预计将来会扩展,也应该考虑使用策略模式。