
1. 策略模式在C中的核心价值策略模式是我在大型C项目中用得最多的设计模式之一。它本质上是一种行为设计模式允许在运行时选择算法或行为。想象你正在开发一个支付系统需要支持信用卡、支付宝、微信支付等多种支付方式。如果把这些支付逻辑全部硬编码在一个类里每次新增支付方式都要修改原有代码这显然违反了开闭原则。策略模式通过将算法封装在独立的策略类中来解决这个问题。在支付系统的例子中我们可以定义一个支付策略接口然后为每种支付方式实现具体的策略类。主程序只需要持有策略接口的指针或引用运行时动态切换具体策略。class PaymentStrategy { public: virtual ~PaymentStrategy() default; virtual void pay(int amount) 0; }; class CreditCardStrategy : public PaymentStrategy { public: void pay(int amount) override { // 信用卡支付的具体实现 } }; class AlipayStrategy : public PaymentStrategy { public: void pay(int amount) override { // 支付宝支付的具体实现 } };这种设计带来的最大好处是解耦。策略的使用者不需要知道具体策略的实现细节只需要通过统一的接口调用。当需要新增支付方式时只需添加新的策略类完全不需要修改现有代码。2. 高级策略模式的实现技巧2.1 策略工厂与注册机制在实际项目中我们通常需要更灵活的策略管理方式。我常用的方法是结合工厂模式和注册机制。下面是一个典型的实现class StrategyFactory { public: using Creator std::functionstd::unique_ptrStrategy(); static StrategyFactory instance() { static StrategyFactory factory; return factory; } void registerStrategy(const std::string name, Creator creator) { creators_[name] creator; } std::unique_ptrStrategy create(const std::string name) { auto it creators_.find(name); if (it ! creators_.end()) { return it-second(); } return nullptr; } private: std::unordered_mapstd::string, Creator creators_; }; // 注册策略 struct StrategyRegistrar { StrategyRegistrar(const std::string name, StrategyFactory::Creator creator) { StrategyFactory::instance().registerStrategy(name, creator); } }; #define REGISTER_STRATEGY(name, class) \ static StrategyRegistrar registrar_##class( \ name, []() { return std::make_uniqueclass(); })这种实现允许我们在任何地方注册新的策略使用时只需要知道策略的名称即可。在插件式架构中特别有用新开发的策略模块可以自行注册主程序完全不需要修改。2.2 策略组合与装饰器模式有时我们需要组合多个策略来实现复杂行为。这时可以结合装饰器模式class CompositeStrategy : public Strategy { public: void addStrategy(std::unique_ptrStrategy strategy) { strategies_.push_back(std::move(strategy)); } void execute() override { for (auto strategy : strategies_) { strategy-execute(); } } private: std::vectorstd::unique_ptrStrategy strategies_; };我在一个图像处理项目中就使用了这种设计。基础策略实现简单的图像变换旋转、缩放等组合策略可以将多个变换按特定顺序应用形成复杂的处理流水线。3. 性能优化与线程安全考量3.1 策略对象的重用频繁创建和销毁策略对象可能带来性能问题。对于无状态的策略我们可以使用享元模式共享实例class StatelessStrategy : public Strategy { public: void execute() override { // 无状态实现 } }; class StrategyCache { public: static Strategy getInstance() { static StatelessStrategy instance; return instance; } };对于有状态的策略可以考虑对象池模式。我在一个高频交易系统中就使用了这种优化将策略对象预先创建并放入池中使用时借用用完后归还。3.2 线程安全策略在多线程环境中使用策略模式需要特别注意class ThreadSafeContext { public: void setStrategy(std::unique_ptrStrategy strategy) { std::lock_guardstd::mutex lock(mutex_); strategy_ std::move(strategy); } void executeStrategy() { std::lock_guardstd::mutex lock(mutex_); if (strategy_) { strategy_-execute(); } } private: std::mutex mutex_; std::unique_ptrStrategy strategy_; };如果策略本身有共享数据也需要确保策略内部的线程安全。我通常会采用以下原则尽量设计无状态策略必须的状态尽量通过参数传递无法避免的共享状态使用适当的同步机制4. 实际项目中的经验教训4.1 策略与模板的抉择在C中我们还可以使用模板来实现策略模式template typename Strategy class Context { public: void execute() { strategy_.execute(); } private: Strategy strategy_; };这种编译期策略的优点是零运行时开销缺点是策略不能在运行时改变。我的经验法则是如果策略在程序生命周期内不变使用模板需要运行时动态切换使用传统策略模式性能关键路径考虑模板灵活性要求高时用运行时策略4.2 策略模式的滥用与节制虽然策略模式很强大但也要避免过度使用。我曾经参与重构的一个项目几乎每个类都有一个策略接口导致系统过于复杂。后来我们通过以下方式简化合并相关策略将一些简单策略改为普通函数移除实际只有一种实现的策略接口策略模式最适合以下场景一个操作有多个变体需要在运行时选择算法算法可能在未来扩展需要将算法的实现与使用分离4.3 测试策略模式策略模式的一个附带好处是便于测试。我们可以为测试创建mock策略class MockStrategy : public Strategy { public: MOCK_METHOD(void, execute, (), (override)); }; TEST(ContextTest, ExecuteStrategy) { MockStrategy mock; Context context; EXPECT_CALL(mock, execute()).Times(1); context.setStrategy(mock); context.executeStrategy(); }这种测试方式可以完全隔离策略的具体实现专注于测试上下文类的行为。我在TDD实践中发现策略模式天然适合单元测试因为每个策略都可以独立测试上下文类也容易通过mock策略来测试。5. C20中的新特性应用现代C为策略模式带来了新的可能性。比如使用concept约束策略类型template typename S concept StrategyConcept requires(S s) { { s.execute() } - std::same_asvoid; }; template StrategyConcept S class ModernContext { // ... };这样可以在编译期确保模板参数符合策略接口要求比传统的运行时多态更安全。我在最近的一个项目中就使用了这种方法结合static_assert提供友好的错误信息。另一个有用的特性是std::function作为策略接口的轻量级替代class FunctionContext { public: using Strategy std::functionvoid(); void setStrategy(Strategy strategy) { strategy_ std::move(strategy); } void execute() { if (strategy_) { strategy_(); } } private: Strategy strategy_; };这种方式适用于简单的策略避免了定义接口类的开销。但要注意对于复杂的策略体系传统的接口类方式仍然更有优势因为它提供了明确的类型层次和文档化。