C++策略模式详解:实现方式与应用场景
2026/9/14 21:14:14 网站建设 项目流程

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 过度设计陷阱

不是所有算法变化都需要策略模式:

  • 简单条件判断足够时不要用策略
  • 算法很少变化时可能不需要
  • 权衡模式引入的复杂度

实际经验:在大型长期维护的项目中,即使当前只有两种算法,如果预计将来会扩展,也应该考虑使用策略模式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询