1. 策略模式基础概念解析
策略模式(Strategy Pattern)是面向对象编程中最常用的设计模式之一,属于行为型模式。它的核心思想是将算法族封装起来,使它们可以相互替换,让算法的变化独立于使用算法的客户端。
在C++中实现策略模式时,我们通常会遇到以下几个关键组件:
- 抽象策略类(Strategy):定义所有支持的算法的公共接口
- 具体策略类(ConcreteStrategy):实现抽象策略定义的接口
- 上下文类(Context):持有一个策略类的引用,最终给客户端调用
实际开发中,策略模式特别适合处理那些在运行时需要切换不同算法或业务规则的场景。比如电商平台的折扣策略、游戏中的AI行为策略等。
2. 经典策略模式实现
2.1 基础实现框架
让我们从一个简单的排序策略示例开始:
cpp复制// 抽象策略类
class SortStrategy {
public:
virtual void sort(vector<int>& data) = 0;
virtual ~SortStrategy() = default;
};
// 具体策略类:快速排序
class QuickSort : public SortStrategy {
public:
void sort(vector<int>& data) override {
cout << "使用快速排序算法" << endl;
// 实际快速排序实现...
}
};
// 具体策略类:归并排序
class MergeSort : public SortStrategy {
public:
void sort(vector<int>& data) override {
cout << "使用归并排序算法" << endl;
// 实际归并排序实现...
}
};
// 上下文类
class Sorter {
private:
unique_ptr<SortStrategy> strategy;
public:
explicit Sorter(unique_ptr<SortStrategy> strategy)
: strategy(std::move(strategy)) {}
void setStrategy(unique_ptr<SortStrategy> newStrategy) {
strategy = std::move(newStrategy);
}
void executeSort(vector<int>& data) {
strategy->sort(data);
}
};
2.2 客户端调用示例
cpp复制int main() {
vector<int> data = {5, 2, 7, 1, 9};
Sorter sorter(make_unique<QuickSort>());
sorter.executeSort(data); // 使用快速排序
sorter.setStrategy(make_unique<MergeSort>());
sorter.executeSort(data); // 切换到归并排序
return 0;
}
3. 现代C++中的策略模式优化
3.1 使用函数对象替代类层次
C++11及以后版本中,我们可以使用std::function和lambda表达式简化策略模式的实现:
cpp复制class Sorter {
private:
function<void(vector<int>&)> strategy;
public:
explicit Sorter(function<void(vector<int>&)> strategy)
: strategy(strategy) {}
void setStrategy(function<void(vector<int>&)> newStrategy) {
strategy = newStrategy;
}
void executeSort(vector<int>& data) {
strategy(data);
}
};
// 使用示例
int main() {
vector<int> data = {5, 2, 7, 1, 9};
auto quickSort = [](vector<int>& data) {
cout << "Lambda快速排序" << endl;
// 实现...
};
Sorter sorter(quickSort);
sorter.executeSort(data);
return 0;
}
3.2 模板策略模式
对于编译时确定的策略,可以使用模板实现:
cpp复制template<typename Strategy>
class Sorter {
private:
Strategy strategy;
public:
void executeSort(vector<int>& data) {
strategy.sort(data);
}
};
// 使用示例
struct BubbleSort {
void sort(vector<int>& data) {
cout << "冒泡排序" << endl;
// 实现...
}
};
int main() {
vector<int> data = {5, 2, 7, 1, 9};
Sorter<BubbleSort> sorter;
sorter.executeSort(data);
return 0;
}
4. 实战应用案例
4.1 游戏AI行为策略
cpp复制// 行为策略接口
class AIBehavior {
public:
virtual void execute() = 0;
virtual ~AIBehavior() = default;
};
// 具体策略
class AggressiveBehavior : public AIBehavior {
public:
void execute() override {
cout << "执行攻击性行为" << endl;
// 攻击逻辑...
}
};
class DefensiveBehavior : public AIBehavior {
public:
void execute() override {
cout << "执行防御性行为" << endl;
// 防御逻辑...
}
};
// AI角色类
class AICharacter {
private:
unique_ptr<AIBehavior> behavior;
public:
void setBehavior(unique_ptr<AIBehavior> newBehavior) {
behavior = std::move(newBehavior);
}
void performAction() {
if(behavior) {
behavior->execute();
}
}
};
4.2 电商折扣策略
cpp复制class DiscountStrategy {
public:
virtual double applyDiscount(double originalPrice) = 0;
virtual ~DiscountStrategy() = default;
};
class NoDiscount : public DiscountStrategy {
public:
double applyDiscount(double originalPrice) override {
return originalPrice;
}
};
class PercentageDiscount : public DiscountStrategy {
private:
double percentage;
public:
explicit PercentageDiscount(double percent) : percentage(percent) {}
double applyDiscount(double originalPrice) override {
return originalPrice * (1 - percentage/100);
}
};
class FixedAmountDiscount : public DiscountStrategy {
private:
double amount;
public:
explicit FixedAmountDiscount(double amt) : amount(amt) {}
double applyDiscount(double originalPrice) override {
return max(0.0, originalPrice - amount);
}
};
class ShoppingCart {
private:
vector<double> prices;
unique_ptr<DiscountStrategy> discount;
public:
void setDiscountStrategy(unique_ptr<DiscountStrategy> strategy) {
discount = std::move(strategy);
}
void addItem(double price) {
prices.push_back(price);
}
double calculateTotal() {
double sum = accumulate(prices.begin(), prices.end(), 0.0);
if(discount) {
return discount->applyDiscount(sum);
}
return sum;
}
};
5. 策略模式的优缺点与适用场景
5.1 主要优点
- 开闭原则:可以在不修改上下文的情况下引入新策略
- 消除条件语句:避免使用大量的if-else或switch-case语句
- 算法复用:相同策略可以被不同上下文使用
- 运行时灵活性:可以在运行时切换算法
5.2 潜在缺点
- 客户端必须了解不同策略:客户端需要知道有哪些策略以及它们的区别
- 增加对象数量:每个策略都是一个类,可能增加系统复杂度
- 通信开销:策略与上下文之间可能需要交换数据
5.3 最佳适用场景
- 一个系统需要在几种算法中动态切换
- 需要避免暴露复杂的、与算法相关的数据结构
- 一个类定义了多种行为,并且这些行为以多个条件语句的形式出现
6. 常见问题与解决方案
6.1 策略选择问题
问题:如何让客户端选择合适的策略?
解决方案:
- 使用工厂模式创建策略对象
- 提供策略描述的元信息
- 实现策略的自动选择机制
cpp复制class StrategyFactory {
public:
static unique_ptr<DiscountStrategy> createDiscountStrategy(const string& type, double value) {
if(type == "percentage") {
return make_unique<PercentageDiscount>(value);
} else if(type == "fixed") {
return make_unique<FixedAmountDiscount>(value);
}
return make_unique<NoDiscount>();
}
};
6.2 策略间共享数据
问题:多个策略需要共享相同的数据怎么办?
解决方案:
- 将共享数据作为上下文的一部分
- 使用参数对象传递数据
- 实现策略间的通信接口
cpp复制struct StrategyContext {
double totalPurchases;
int customerLevel;
// 其他共享数据...
};
class LoyaltyDiscount : public DiscountStrategy {
public:
double applyDiscount(double originalPrice, const StrategyContext& context) {
if(context.customerLevel > 5) {
return originalPrice * 0.9; // VIP客户9折
}
return originalPrice;
}
};
6.3 性能优化技巧
- 策略对象复用:对于无状态的策略,可以共享同一个实例
- 小对象优化:使用函数对象而非类层次结构
- 编译时策略:对于不会变化的策略,使用模板实现
- 策略缓存:缓存策略计算结果避免重复计算
7. 高级应用技巧
7.1 策略组合模式
可以将多个策略组合起来形成更复杂的行为:
cpp复制class CompositeDiscount : public DiscountStrategy {
private:
vector<unique_ptr<DiscountStrategy>> strategies;
public:
void addStrategy(unique_ptr<DiscountStrategy> strategy) {
strategies.push_back(std::move(strategy));
}
double applyDiscount(double originalPrice) override {
double price = originalPrice;
for(auto& strategy : strategies) {
price = strategy->applyDiscount(price);
}
return price;
}
};
7.2 策略与模板元编程
对于性能关键的场景,可以使用模板元编程实现编译时策略选择:
cpp复制template<typename Strategy>
class AlgorithmExecutor {
private:
Strategy strategy;
public:
template<typename... Args>
auto execute(Args&&... args) {
return strategy(std::forward<Args>(args)...);
}
};
// 使用示例
struct FastAlgorithm {
int operator()(int a, int b) const {
return a + b; // 简化示例
}
};
int main() {
AlgorithmExecutor<FastAlgorithm> executor;
cout << executor.execute(3, 4) << endl;
return 0;
}
7.3 策略模式的测试策略
为策略模式设计单元测试时,可以考虑:
- 测试每个具体策略的独立行为
- 测试上下文类与策略的交互
- 测试策略切换的正确性
- 测试边界条件和异常情况
cpp复制TEST(StrategyPatternTest, TestDiscountStrategies) {
PercentageDiscount tenPercent(10);
ASSERT_EQ(90, tenPercent.applyDiscount(100));
FixedAmountDiscount twentyOff(20);
ASSERT_EQ(80, twentyOff.applyDiscount(100));
ShoppingCart cart;
cart.addItem(50);
cart.addItem(50);
cart.setDiscountStrategy(make_unique<PercentageDiscount>(10));
ASSERT_EQ(90, cart.calculateTotal());
}
8. 实际项目中的经验分享
在实际C++项目中使用策略模式时,我总结了一些有价值的经验:
-
策略对象的生命周期管理:
- 优先使用unique_ptr管理策略对象
- 对于需要共享的策略,使用shared_ptr
- 避免裸指针,防止内存泄漏
-
策略接口设计原则:
- 保持策略接口精简
- 避免策略接口过于庞大
- 考虑将相关策略分组到不同接口中
-
性能考量:
- 虚函数调用有开销,对性能关键路径要谨慎
- 考虑使用CRTP模式减少虚函数开销
- 对小对象使用std::function可能比多态更高效
-
与其它模式的协作:
- 策略模式常与工厂模式一起使用
- 可以组合使用策略模式和装饰器模式
- 策略模式是模板方法模式的替代方案
-
现代C++特性应用:
- 使用移动语义提高效率
- 考虑使用type-erasure技术
- 利用constexpr实现编译时策略选择
在大型项目中,策略模式的一个常见陷阱是策略类爆炸。为避免这个问题,可以考虑使用参数化策略,或者将简单策略实现为函数对象而非完整类。
