1. 策略模式基础与C++实现要点
策略模式作为行为型设计模式的经典代表,其核心思想是将算法族进行封装,使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端。在C++中实现策略模式时,我们需要特别注意语言特性带来的实现差异。
1.1 经典策略模式结构
典型的策略模式包含三个核心组件:
- Context(上下文):维护对具体策略的引用,通过策略接口与策略对象交互
- Strategy(策略接口):定义所有支持算法的公共接口
- ConcreteStrategy(具体策略):实现策略接口的具体算法类
C++实现中通常会使用抽象类作为策略接口,通过纯虚函数定义算法契约。以下是一个支付策略的简单示例:
cpp复制class PaymentStrategy {
public:
virtual ~PaymentStrategy() = default;
virtual void pay(int amount) = 0;
};
class CreditCardPayment : public PaymentStrategy {
public:
void pay(int amount) override {
std::cout << "Processing credit card payment of $" << amount << std::endl;
}
};
class PayPalPayment : public PaymentStrategy {
public:
void pay(int amount) override {
std::cout << "Processing PayPal payment of $" << amount << std::endl;
}
};
class PaymentContext {
private:
std::unique_ptr<PaymentStrategy> strategy;
public:
void setStrategy(std::unique_ptr<PaymentStrategy> strategy) {
this->strategy = std::move(strategy);
}
void executePayment(int amount) {
if(strategy) {
strategy->pay(amount);
}
}
};
1.2 C++实现特性考量
在C++中实现策略模式时,有几个关键点需要特别注意:
- 对象生命周期管理:推荐使用智能指针(如
std::unique_ptr)管理策略对象,避免内存泄漏 - 性能考量:频繁创建销毁策略对象可能影响性能,可考虑对象池技术
- 多线程安全:如果策略对象会被多线程共享,需要确保线程安全
- 内联优化:对于简单策略,使用函数对象可能获得更好的性能
提示:在性能敏感场景中,可以考虑将策略实现为无状态的函数对象,这样编译器更容易进行内联优化。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C++策略模式的高级变体
2.1 基于模板的策略模式
C++的模板特性允许我们在编译时绑定策略,这种静态策略模式可以完全消除运行时多态的开销:
cpp复制template<typename Strategy>
class PaymentProcessor {
private:
Strategy strategy;
public:
void processPayment(int amount) {
strategy.pay(amount);
}
};
struct BitcoinPayment {
void pay(int amount) {
std::cout << "Processing Bitcoin payment of " << amount << " satoshis" << std::endl;
}
};
// 使用示例
PaymentProcessor<BitcoinPayment> processor;
processor.processPayment(100);
这种方式的优势在于:
- 零运行时开销
- 编译时类型检查
- 更好的内联优化机会
缺点是失去了运行时动态切换策略的能力。
2.2 策略与STL算法的结合
我们可以将策略模式的思想应用于STL算法,创建可定制的算法策略。例如,实现一个可配置的排序策略:
cpp复制class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) = 0;
};
class QuickSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::sort(data.begin(), data.end());
}
};
class BubbleSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
for(size_t i = 0; i < data.size(); ++i) {
for(size_t j = i+1; j < data.size(); ++j) {
if(data[i] > data[j]) {
std::swap(data[i], data[j]);
}
}
}
}
};
template<typename Container, typename Strategy>
void sortContainer(Container& c, Strategy&& s) {
s.sort(c);
}
2.3 策略模式的函数式实现
C++11引入的lambda表达式和std::function为策略模式提供了更灵活的实现方式:
cpp复制class FunctionStrategy {
public:
using Strategy = std::function<void(int)>;
void setStrategy(Strategy s) {
strategy = s;
}
void execute(int value) {
if(strategy) {
strategy(value);
}
}
private:
Strategy strategy;
};
// 使用示例
FunctionStrategy fs;
fs.setStrategy([](int x) { std::cout << "Lambda strategy: " << x << std::endl; });
fs.execute(42);
这种方式特别适合一次性使用的简单策略,避免了创建完整类层次结构的开销。
3. 策略模式在C++项目中的实际应用
3.1 游戏开发中的AI行为策略
在游戏开发中,策略模式常用于实现NPC的不同行为模式。例如,敌人AI可以根据玩家距离切换攻击策略:
cpp复制class AttackStrategy {
public:
virtual ~AttackStrategy() = default;
virtual void attack() = 0;
};
class MeleeAttack : public AttackStrategy {
public:
void attack() override {
std::cout << "Performing melee attack!" << std::endl;
}
};
class RangedAttack : public AttackStrategy {
public:
void attack() override {
std::cout << "Firing ranged weapon!" << std::endl;
}
};
class EnemyAI {
private:
std::unique_ptr<AttackStrategy> strategy;
float distanceToPlayer;
void updateStrategy() {
if(distanceToPlayer < 5.0f) {
strategy = std::make_unique<MeleeAttack>();
} else {
strategy = std::make_unique<RangedAttack>();
}
}
public:
void setDistance(float distance) {
distanceToPlayer = distance;
updateStrategy();
}
void performAttack() {
if(strategy) {
strategy->attack();
}
}
};
3.2 金融计算中的定价策略
在量化金融领域,不同的金融产品可能需要不同的定价策略:
cpp复制class PricingStrategy {
public:
virtual ~PricingStrategy() = default;
virtual double calculatePrice(const TradeData& data) = 0;
};
class BlackScholesStrategy : public PricingStrategy {
public:
double calculatePrice(const TradeData& data) override {
// 实现Black-Scholes模型计算
return 0.0;
}
};
class MonteCarloStrategy : public PricingStrategy {
public:
double calculatePrice(const TradeData& data) override {
// 实现蒙特卡洛模拟
return 0.0;
}
};
class PricingEngine {
private:
std::map<std::string, std::unique_ptr<PricingStrategy>> strategies;
public:
void addStrategy(const std::string& name, std::unique_ptr<PricingStrategy> strategy) {
strategies[name] = std::move(strategy);
}
double price(const std::string& strategyName, const TradeData& data) {
if(auto it = strategies.find(strategyName); it != strategies.end()) {
return it->second->calculatePrice(data);
}
throw std::runtime_error("Unknown pricing strategy");
}
};
3.3 日志系统中的输出策略
日志系统通常需要支持多种输出方式,策略模式在这里非常适用:
cpp复制class LogStrategy {
public:
virtual ~LogStrategy() = default;
virtual void log(const std::string& message) = 0;
};
class ConsoleLogStrategy : public LogStrategy {
public:
void log(const std::string& message) override {
std::cout << "[CONSOLE] " << message << std::endl;
}
};
class FileLogStrategy : public LogStrategy {
public:
explicit FileLogStrategy(const std::string& filename) : outfile(filename) {}
void log(const std::string& message) override {
outfile << "[FILE] " << message << std::endl;
}
private:
std::ofstream outfile;
};
class NetworkLogStrategy : public LogStrategy {
public:
void log(const std::string& message) override {
// 实现网络日志发送
}
};
class Logger {
private:
std::unique_ptr<LogStrategy> strategy;
public:
void setStrategy(std::unique_ptr<LogStrategy> s) {
strategy = std::move(s);
}
void log(const std::string& message) {
if(strategy) {
strategy->log(message);
}
}
};
4. 策略模式的优化与陷阱
4.1 性能优化技巧
- 策略对象复用:对于无状态策略,可以共享单个实例
- 小对象优化:使用
std::function的small buffer optimization - 编译时策略:对于不常变化的策略,考虑模板实现
- 策略缓存:缓存策略计算结果,避免重复计算
cpp复制// 策略对象复用示例
class StrategyCache {
private:
std::unordered_map<std::string, std::shared_ptr<PricingStrategy>> cache;
public:
std::shared_ptr<PricingStrategy> getStrategy(const std::string& name) {
if(auto it = cache.find(name); it != cache.end()) {
return it->second;
}
std::shared_ptr<PricingStrategy> strategy;
if(name == "BlackScholes") {
strategy = std::make_shared<BlackScholesStrategy>();
} else if(name == "MonteCarlo") {
strategy = std::make_shared<MonteCarloStrategy>();
}
if(strategy) {
cache[name] = strategy;
}
return strategy;
}
};
4.2 常见陷阱与解决方案
-
策略接口膨胀:
- 问题:策略接口包含太多方法,导致具体策略必须实现不相关的方法
- 解决:将大接口拆分为多个小接口(接口隔离原则)
-
策略选择逻辑复杂:
- 问题:选择合适策略的条件判断过于复杂
- 解决:引入策略工厂或策略管理器封装选择逻辑
-
策略状态管理:
- 问题:策略需要维护状态导致线程安全问题
- 解决:确保策略无状态或提供适当的同步机制
-
策略注册与发现:
- 问题:系统需要动态添加新策略
- 解决:实现策略注册机制,如插件系统
cpp复制// 策略工厂示例
class StrategyFactory {
public:
using StrategyCreator = std::function<std::unique_ptr<PricingStrategy>()>;
void registerCreator(const std::string& name, StrategyCreator creator) {
creators[name] = creator;
}
std::unique_ptr<PricingStrategy> create(const std::string& name) {
if(auto it = creators.find(name); it != creators.end()) {
return it->second();
}
return nullptr;
}
private:
std::unordered_map<std::string, StrategyCreator> creators;
};
// 注册策略
StrategyFactory factory;
factory.registerCreator("BlackScholes", []() {
return std::make_unique<BlackScholesStrategy>();
});
4.3 策略模式与其他模式的结合
策略模式常与其他设计模式配合使用,形成更强大的解决方案:
- 策略+工厂模式:通过工厂创建和管理策略对象
- 策略+装饰器模式:动态添加策略的附加功能
- 策略+组合模式:构建策略的层次结构
- 策略+观察者模式:在策略变化时通知相关对象
cpp复制// 策略与装饰器模式结合示例
class LogStrategyDecorator : public LogStrategy {
protected:
std::unique_ptr<LogStrategy> wrapped;
public:
explicit LogStrategyDecorator(std::unique_ptr<LogStrategy> strategy)
: wrapped(std::move(strategy)) {}
};
class TimestampDecorator : public LogStrategyDecorator {
public:
using LogStrategyDecorator::LogStrategyDecorator;
void log(const std::string& message) override {
auto now = std::chrono::system_clock::now();
auto ts = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&ts), "%Y-%m-%d %H:%M:%S") << " " << message;
wrapped->log(ss.str());
}
};
// 使用装饰后的策略
auto strategy = std::make_unique<TimestampDecorator>(
std::make_unique<ConsoleLogStrategy>()
);
Logger logger;
logger.setStrategy(std::move(strategy));
logger.log("Decorated message");
