在软件开发中,我们经常遇到需要根据不同条件执行不同算法的场景。传统做法可能是使用大量的条件分支语句(if-else或switch-case),但随着需求变化,这种代码会变得难以维护和扩展。策略模式正是为解决这类问题而生的经典设计模式。
策略模式(Strategy Pattern)属于行为型设计模式,其核心思想是将算法家族封装起来,让它们可以互相替换。这种模式让算法的变化独立于使用算法的客户端,带来几个显著优势:
策略模式通常由三个关键组件构成:
策略接口(Strategy Interface):
具体策略类(Concrete Strategies):
上下文类(Context):
这种结构使得算法可以在不影响客户端的情况下自由切换,同时也便于算法的单元测试。
让我们从一个排序策略的经典示例开始,展示策略模式的基本实现方式:
cpp复制#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
// 策略接口:排序策略
class SortingStrategy {
public:
virtual ~SortingStrategy() = default;
virtual void sort(std::vector<int>& data) const = 0;
virtual std::string name() const = 0;
};
// 具体策略:快速排序
class QuickSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Applying QuickSort...\n";
std::sort(data.begin(), data.end()); // 使用标准库的快速排序
}
std::string name() const override { return "QuickSort"; }
};
// 具体策略:归并排序
class MergeSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Applying MergeSort...\n";
if (data.size() <= 1) return;
auto mid = data.begin() + data.size() / 2;
std::vector<int> left(data.begin(), mid);
std::vector<int> right(mid, data.end());
sort(left);
sort(right);
std::merge(left.begin(), left.end(),
right.begin(), right.end(),
data.begin());
}
std::string name() const override { return "MergeSort"; }
};
// 上下文类:排序器
class Sorter {
std::unique_ptr<SortingStrategy> strategy_;
public:
explicit Sorter(std::unique_ptr<SortingStrategy> strategy = nullptr)
: strategy_(std::move(strategy)) {}
void set_strategy(std::unique_ptr<SortingStrategy> strategy) {
strategy_ = std::move(strategy);
}
void sort(std::vector<int>& data) const {
if (!strategy_) {
throw std::runtime_error("No sorting strategy set");
}
strategy_->sort(data);
}
std::string current_strategy_name() const {
return strategy_ ? strategy_->name() : "No strategy set";
}
};
int main() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
Sorter sorter;
// 使用快速排序
sorter.set_strategy(std::make_unique<QuickSort>());
std::cout << "Current strategy: " << sorter.current_strategy_name() << "\n";
sorter.sort(data);
std::cout << "Sorted data: ";
for (int num : data) std::cout << num << " ";
std::cout << "\n\n";
// 重新生成乱序数据
data = {5, 2, 9, 1, 5, 6};
// 切换到归并排序
sorter.set_strategy(std::make_unique<MergeSort>());
std::cout << "Current strategy: " << sorter.current_strategy_name() << "\n";
sorter.sort(data);
std::cout << "Sorted data: ";
for (int num : data) std::cout << num << " ";
std::cout << "\n";
return 0;
}
这个实现展示了策略模式的基本要素:
SortingStrategy 是策略接口QuickSort 和 MergeSort 是具体策略实现Sorter 是上下文类,负责使用策略在C++中,我们需要特别注意资源管理。上述示例使用了std::unique_ptr来管理策略对象生命周期,这有几个好处:
注意:如果需要在多个上下文间共享策略对象,可以使用
std::shared_ptr。但通常策略对象是无状态的,所以每个上下文拥有自己的策略实例更为合理。
C++20引入了多项重要特性,我们可以利用它们来增强策略模式的实现。
C++20的概念(Concepts)允许我们对模板参数施加更严格的约束,这可以用来创建更安全的策略模式实现:
cpp复制#include <concepts>
#include <vector>
// 定义排序策略概念
template <typename T>
concept SortingStrategy = requires(T t, std::vector<int>& v) {
{ t.sort(v) } -> std::same_as<void>;
{ t.name() } -> std::same_as<std::string>;
};
// 使用概念的上下文类
template <SortingStrategy T>
class GenericSorter {
T strategy;
public:
explicit GenericSorter(T strat) : strategy(std::move(strat)) {}
void sort(std::vector<int>& data) {
strategy.sort(data);
}
std::string strategy_name() const {
return strategy.name();
}
};
// 符合概念的具体策略
class BubbleSort {
public:
void sort(std::vector<int>& data) const {
std::cout << "Applying BubbleSort...\n";
for (size_t i = 0; i < data.size(); ++i) {
for (size_t j = 0; j < data.size() - i - 1; ++j) {
if (data[j] > data[j+1]) {
std::swap(data[j], data[j+1]);
}
}
}
}
std::string name() const { return "BubbleSort"; }
};
// 使用示例
int main() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
GenericSorter<BubbleSort> sorter(BubbleSort{});
std::cout << "Using strategy: " << sorter.strategy_name() << "\n";
sorter.sort(data);
// 以下代码会导致编译错误,因为类型不符合概念要求
// struct InvalidStrategy {};
// GenericSorter<InvalidStrategy> invalid; // 错误:InvalidStrategy 不满足 SortingStrategy
}
这种方式的优点:
C++20的范围库提供了更简洁的方式来处理元素序列。我们可以在策略实现中利用它:
cpp复制#include <ranges>
#include <algorithm>
class RangeSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Applying RangeSort...\n";
std::ranges::sort(data);
}
std::string name() const override { return "RangeSort"; }
};
// 更复杂的范围操作策略
class ReverseSort : public SortingStrategy {
public:
void sort(std::vector<int>& data) const override {
std::cout << "Applying ReverseSort...\n";
auto reversed = data | std::views::reverse;
std::ranges::sort(reversed);
}
std::string name() const override { return "ReverseSort"; }
};
范围库使算法实现更简洁,可读性更强,特别是在处理复杂的数据转换时。
C++20的三路比较运算符(<=>)可以用于实现更灵活的比较策略:
cpp复制#include <compare>
class CustomCompareStrategy {
public:
virtual ~CustomCompareStrategy() = default;
virtual std::strong_ordering compare(int a, int b) const = 0;
};
class AscendingCompare : public CustomCompareStrategy {
public:
std::strong_ordering compare(int a, int b) const override {
return a <=> b;
}
};
class DescendingCompare : public CustomCompareStrategy {
public:
std::strong_ordering compare(int a, int b) const override {
return b <=> a;
}
};
class AbsoluteCompare : public CustomCompareStrategy {
public:
std::strong_ordering compare(int a, int b) const override {
return abs(a) <=> abs(b);
}
};
template <typename CompareStrategy>
void sort_with_compare(std::vector<int>& data, const CompareStrategy& comp) {
std::sort(data.begin(), data.end(),
[&comp](int a, int b) { return comp.compare(a, b) < 0; });
}
这种方式提供了更灵活的比较策略实现,可以轻松扩展新的比较方式。
在实际应用中,我们经常需要根据配置或运行时条件创建不同的策略。这时可以结合工厂模式:
cpp复制#include <map>
#include <functional>
#include <stdexcept>
class StrategyFactory {
using Creator = std::function<std::unique_ptr<SortingStrategy>()>;
std::map<std::string, Creator> creators_;
public:
template <typename T>
void register_strategy(const std::string& name) {
creators_[name] = []() { return std::make_unique<T>(); };
}
std::unique_ptr<SortingStrategy> create(const std::string& name) const {
auto it = creators_.find(name);
if (it == creators_.end()) {
throw std::invalid_argument("Unknown strategy: " + name);
}
return it->second();
}
static StrategyFactory& instance() {
static StrategyFactory instance;
return instance;
}
};
// 注册策略
bool register_strategies() {
auto& factory = StrategyFactory::instance();
factory.register_strategy<QuickSort>("quick");
factory.register_strategy<MergeSort>("merge");
factory.register_strategy<BubbleSort>("bubble");
factory.register_strategy<RangeSort>("range");
return true;
}
// 静态注册
static bool registered = register_strategies();
// 使用示例
void demo_factory() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
try {
auto strategy = StrategyFactory::instance().create("quick");
Sorter sorter(std::move(strategy));
sorter.sort(data);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
}
}
这种方式的优点:
对于性能敏感的场景,我们可以使用基于模板的策略模式,完全消除运行时多态开销:
cpp复制template <typename Strategy>
class CompileTimeSorter {
Strategy strategy;
public:
void sort(std::vector<int>& data) {
strategy.sort(data);
}
std::string strategy_name() const {
return Strategy::name(); // 静态方法
}
};
struct QuickSortPolicy {
static void sort(std::vector<int>& data) {
std::sort(data.begin(), data.end());
}
static std::string name() { return "Compile-time QuickSort"; }
};
struct MergeSortPolicy {
static void sort(std::vector<int>& data) {
if (data.size() <= 1) return;
auto mid = data.begin() + data.size() / 2;
std::vector<int> left(data.begin(), mid);
std::vector<int> right(mid, data.end());
sort(left);
sort(right);
std::merge(left.begin(), left.end(),
right.begin(), right.end(),
data.begin());
}
static std::string name() { return "Compile-time MergeSort"; }
};
// 使用示例
void demo_compile_time_strategy() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
CompileTimeSorter<QuickSortPolicy> quick_sorter;
std::cout << "Using: " << quick_sorter.strategy_name() << "\n";
quick_sorter.sort(data);
CompileTimeSorter<MergeSortPolicy> merge_sorter;
std::cout << "Using: " << merge_sorter.strategy_name() << "\n";
merge_sorter.sort(data);
}
这种方式的优缺点:
在选择策略模式实现方式时,需要考虑性能因素:
虚函数开销:传统的基于继承的策略模式会有虚函数调用开销
模板策略:编译时策略没有运行时开销
内存局部性:频繁切换策略可能导致缓存失效
性能优化建议:
在游戏开发中,策略模式常用于实现不同的AI行为:
cpp复制// AI行为策略接口
class AIBehavior {
public:
virtual ~AIBehavior() = default;
virtual void update(GameEntity& entity, float deltaTime) = 0;
};
// 具体策略:巡逻行为
class PatrolBehavior : public AIBehavior {
std::vector<Vector3> waypoints;
size_t currentWaypoint = 0;
public:
explicit PatrolBehavior(std::vector<Vector3> points) : waypoints(std::move(points)) {}
void update(GameEntity& entity, float deltaTime) override {
if (waypoints.empty()) return;
const Vector3& target = waypoints[currentWaypoint];
Vector3 direction = (target - entity.position()).normalized();
entity.move(direction * entity.speed() * deltaTime);
if (distance(entity.position(), target) < 1.0f) {
currentWaypoint = (currentWaypoint + 1) % waypoints.size();
}
}
};
// 具体策略:追击玩家行为
class ChasePlayerBehavior : public AIBehavior {
public:
void update(GameEntity& entity, float deltaTime) override {
Vector3 playerPos = GameWorld::instance().player().position();
Vector3 direction = (playerPos - entity.position()).normalized();
entity.move(direction * entity.speed() * deltaTime);
}
};
// 具体策略:逃跑行为
class FleeBehavior : public AIBehavior {
public:
void update(GameEntity& entity, float deltaTime) override {
Vector3 playerPos = GameWorld::instance().player().position();
Vector3 direction = (entity.position() - playerPos).normalized();
entity.move(direction * entity.speed() * deltaTime);
}
};
// AI控制器(上下文)
class AIController {
std::unique_ptr<AIBehavior> behavior_;
public:
void set_behavior(std::unique_ptr<AIBehavior> behavior) {
behavior_ = std::move(behavior);
}
void update(GameEntity& entity, float deltaTime) {
if (behavior_) {
behavior_->update(entity, deltaTime);
}
}
};
这种设计允许:
在金融软件中,策略模式可用于实现不同的定价算法:
cpp复制// 定价策略接口
class PricingStrategy {
public:
virtual ~PricingStrategy() = default;
virtual double calculatePrice(const Trade& trade) const = 0;
};
// 具体策略:简单加权定价
class WeightedPricing : public PricingStrategy {
double weight_;
public:
explicit WeightedPricing(double weight) : weight_(weight) {}
double calculatePrice(const Trade& trade) const override {
return trade.marketPrice() * weight_;
}
};
// 具体策略:时间衰减定价
class TimeDecayPricing : public PricingStrategy {
double decayRate_;
public:
explicit TimeDecayPricing(double decayRate) : decayRate_(decayRate) {}
double calculatePrice(const Trade& trade) const override {
double timeFactor = exp(-decayRate_ * trade.timeToMaturity());
return trade.marketPrice() * timeFactor;
}
};
// 具体策略:波动率调整定价
class VolatilityAdjustedPricing : public PricingStrategy {
double volatilityScale_;
public:
explicit VolatilityAdjustedPricing(double scale) : volatilityScale_(scale) {}
double calculatePrice(const Trade& trade) const override {
double adjustment = 1.0 + volatilityScale_ * trade.volatility();
return trade.marketPrice() * adjustment;
}
};
// 定价引擎(上下文)
class PricingEngine {
std::unique_ptr<PricingStrategy> strategy_;
public:
explicit PricingEngine(std::unique_ptr<PricingStrategy> strategy)
: strategy_(std::move(strategy)) {}
void setStrategy(std::unique_ptr<PricingStrategy> strategy) {
strategy_ = std::move(strategy);
}
double calculatePrice(const Trade& trade) const {
if (!strategy_) {
throw std::logic_error("No pricing strategy set");
}
return strategy_->calculatePrice(trade);
}
};
在网络编程中,可以根据内容类型和网络条件选择不同的压缩策略:
cpp复制// 压缩策略接口
class CompressionStrategy {
public:
virtual ~CompressionStrategy() = default;
virtual std::vector<uint8_t> compress(const std::vector<uint8_t>& data) = 0;
virtual std::vector<uint8_t> decompress(const std::vector<uint8_t>& data) = 0;
virtual std::string name() const = 0;
};
// 具体策略:Zlib压缩
class ZlibCompression : public CompressionStrategy {
int level_;
public:
explicit ZlibCompression(int level = 6) : level_(level) {}
std::vector<uint8_t> compress(const std::vector<uint8_t>& data) override {
// 实际实现使用zlib库
std::cout << "Compressing with Zlib (level " << level_ << ")\n";
return data; // 简化为示例
}
std::vector<uint8_t> decompress(const std::vector<uint8_t>& data) override {
std::cout << "Decompressing with Zlib\n";
return data; // 简化为示例
}
std::string name() const override { return "Zlib"; }
};
// 具体策略:LZ4压缩
class LZ4Compression : public CompressionStrategy {
public:
std::vector<uint8_t> compress(const std::vector<uint8_t>& data) override {
std::cout << "Compressing with LZ4\n";
return data; // 简化为示例
}
std::vector<uint8_t> decompress(const std::vector<uint8_t>& data) override {
std::cout << "Decompressing with LZ4\n";
return data; // 简化为示例
}
std::string name() const override { return "LZ4"; }
};
// 网络发送器(上下文)
class NetworkSender {
std::unique_ptr<CompressionStrategy> compressor_;
public:
void setCompressor(std::unique_ptr<CompressionStrategy> compressor) {
compressor_ = std::move(compressor);
}
void sendData(const std::vector<uint8_t>& data) {
if (!compressor_) {
// 不压缩直接发送
rawSend(data);
return;
}
auto compressed = compressor_->compress(data);
if (compressed.size() < data.size()) {
std::cout << "Using " << compressor_->name()
<< " compression: " << data.size()
<< " -> " << compressed.size() << " bytes\n";
rawSend(compressed);
} else {
std::cout << "Compression not effective, sending raw\n";
rawSend(data);
}
}
private:
void rawSend(const std::vector<uint8_t>& data) {
// 实际网络发送实现
}
};
策略模式和状态模式在结构上相似,但意图不同:
| 特性 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 封装可互换的算法 | 封装与状态相关的行为 |
| 变化 | 策略是外部指定的 | 状态是内部转换的 |
| 知晓性 | 客户端通常知道不同策略 | 客户端不知道具体状态 |
| 生命周期 | 策略通常由客户端创建和设置 | 状态通常由上下文自动管理 |
| 示例 | 排序算法、压缩算法 | 订单状态、游戏角色状态 |
策略模式和模板方法模式都用于封装算法,但方式不同:
模板方法模式:
策略模式:
选择建议:
策略模式和命令模式都涉及封装行为,但侧重点不同:
命令模式:
策略模式:
策略模式最适合以下场景:
策略接口设计:
策略对象传递:
共享策略状态:
性能考量:
策略膨胀:
上下文过于复杂:
策略间通信困难:
过度设计:
下面是一个综合运用C++20特性的完整策略模式示例,展示现代C++的实现方式:
cpp复制#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <concepts>
#include <ranges>
#include <functional>
// 策略接口定义
template <typename T>
concept SortingStrategy = requires(T t, std::vector<int>& v) {
{ t.sort(v) } -> std::same_as<void>;
{ t.name() } -> std::same_as<std::string>;
};
// 传统虚函数实现的策略接口(兼容旧代码)
class ISortingStrategy {
public:
virtual ~ISortingStrategy() = default;
virtual void sort(std::vector<int>& data) const = 0;
virtual std::string name() const = 0;
};
// 适配器:将传统策略适配到概念
template <typename T>
requires std::derived_from<T, ISortingStrategy>
class LegacyStrategyAdapter {
T strategy;
public:
void sort(std::vector<int>& data) { strategy.sort(data); }
std::string name() const { return strategy.name(); }
};
// 具体策略:标准库快速排序
class StdQuickSort {
public:
void sort(std::vector<int>& data) const {
std::sort(data.begin(), data.end());
}
std::string name() const { return "StdQuickSort"; }
};
// 具体策略:范围库排序
class RangeSort {
public:
void sort(std::vector<int>& data) const {
std::ranges::sort(data);
}
std::string name() const { return "RangeSort"; }
};
// 具体策略:并行排序
class ParallelSort {
public:
void sort(std::vector<int>& data) const {
std::sort(std::execution::par, data.begin(), data.end());
}
std::string name() const { return "ParallelSort"; }
};
// 传统策略实现
class BubbleSort : public ISortingStrategy {
public:
void sort(std::vector<int>& data) const override {
for (size_t i = 0; i < data.size(); ++i) {
for (size_t j = 0; j < data.size() - i - 1; ++j) {
if (data[j] > data[j+1]) {
std::swap(data[j], data[j+1]);
}
}
}
}
std::string name() const override { return "BubbleSort"; }
};
// 上下文类(支持概念和传统策略)
class AdvancedSorter {
std::unique_ptr<ISortingStrategy> legacy_strategy_;
public:
// 传统策略接口
void set_legacy_strategy(std::unique_ptr<ISortingStrategy> strategy) {
legacy_strategy_ = std::move(strategy);
}
// 概念策略接口
template <SortingStrategy T>
void sort_with(T strategy, std::vector<int>& data) {
std::cout << "Using " << strategy.name() << "\n";
strategy.sort(data);
}
// 传统策略调用
void sort_legacy(std::vector<int>& data) {
if (legacy_strategy_) {
std::cout << "Using " << legacy_strategy_->name() << "\n";
legacy_strategy_->sort(data);
} else {
throw std::runtime_error("No legacy strategy set");
}
}
// 适配传统策略到概念
template <typename T>
requires std::derived_from<T, ISortingStrategy>
void sort_with_adapted(std::unique_ptr<T> strategy, std::vector<int>& data) {
LegacyStrategyAdapter<T> adapter{*strategy};
sort_with(adapter, data);
}
};
// 策略工厂(现代C++版本)
class StrategyFactory {
public:
template <SortingStrategy T>
static T create() { return T{}; }
template <typename T>
requires std::derived_from<T, ISortingStrategy>
static std::unique_ptr<T> create_legacy() { return std::make_unique<T>(); }
};
// 使用示例
int main() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
AdvancedSorter sorter;
// 使用概念策略
sorter.sort_with(StdQuickSort{}, data);
print_data(data);
sorter.sort_with(RangeSort{}, data);
print_data(data);
// 使用传统策略
sorter.set_legacy_strategy(StrategyFactory::create_legacy<BubbleSort>());
sorter.sort_legacy(data);
print_data(data);
// 适配传统策略到概念
sorter.sort_with_adapted(StrategyFactory::create_legacy<BubbleSort>(), data);
print_data(data);
// 使用并行策略
sorter.sort_with(ParallelSort{}, data);
print_data(data);
}
void print_data(const std::vector<int>& data) {
std::cout << "Data: ";
for (int num : data) std::cout << num << " ";
std::cout << "\n\n";
}
这个示例展示了:
编写测试是确保策略模式正确实现的关键。以下是使用Catch2测试框架的示例:
cpp复制#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include "sorting_strategies.h"
TEST_CASE("Sorting strategies work correctly") {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
std::vector<int> expected = {1, 2, 5, 5, 6, 9};
SECTION("StdQuickSort") {
StdQuickSort s;
s.sort(data);
REQUIRE(data == expected);
}
SECTION("RangeSort") {
RangeSort s;
s.sort(data);
REQUIRE(data == expected);
}
SECTION("ParallelSort") {
ParallelSort s;
s.sort(data);
REQUIRE(data == expected);
}
SECTION("BubbleSort") {
BubbleSort s;
s.sort(data);
REQUIRE(data == expected);
}
}
TEST_CASE("AdvancedSorter works with all strategy types") {
AdvancedSorter sorter;
std::vector<int> data = {5, 2, 9, 1, 5, 6};
std::vector<int> expected = {1, 2, 5, 5, 6, 9};
SECTION("With concept strategy") {
sorter.sort_with(StdQuickSort{}, data);
REQUIRE(data == expected);
}
SECTION("With legacy strategy") {
sorter.set_legacy_strategy(std::make_unique<BubbleSort>());
sorter.sort_legacy(data);
REQUIRE(data == expected);
}
SECTION("With adapted legacy strategy") {
sorter.sort_with_adapted(std::make_unique<BubbleSort>(), data);
REQUIRE(data == expected);
}
}
TEST_CASE("StrategyFactory creates correct strategies") {
SECTION("Creates concept strategies") {
auto strategy = StrategyFactory::create<StdQuickSort>();
REQUIRE(strategy.name() == "StdQuickSort");
}
SECTION("Creates legacy strategies") {
auto strategy = StrategyFactory::create_legacy<BubbleSort>();
REQUIRE(strategy->name() == "BubbleSort");
}
}
随着C++语言的演进,策略模式也有新的实现方式和应用场景:
即将到来的C++23标准可能带来更多改进策略模式实现的特性:
现代C++越来越支持函数式编程风格,策略模式可以与之结合:
cpp复制// 使用std::function作为策略
using SortingStrategy = std::function<void(std::vector<int>&)>;
void quick_sort(std::vector<int>& data) {
std::sort(data.begin(), data.end());
}
void sort_with_strategy(std::vector<int>& data, SortingStrategy strategy) {
strategy(data);
}
// 使用lambda作为策略
auto reverse_sort = [](std::vector<int>& data) {
std::sort(data.rbegin(), data.rend());
};
// 使用示例
void demo_functional_strategy() {
std::vector<int> data = {5, 2, 9, 1, 5, 6};
sort_with_strategy(data, quick_sort);
sort_with_strategy(data, reverse_sort);
// 临时lambda策略
sort_with_strategy(data, [](auto& d) {
std::sort(d.begin(), d.end(), std::greater{});
});
}
这种方式的优点:
结合模板元编程可以实现编译时策略选择和组合:
cpp复制template <typename... Strategies>
struct StrategyPipeline : Strategies... {
using Strategies::operator()...;
};
template <typename... Strategies>
StrategyPipeline<Strategies...> make_pipeline(Strategies... strategies) {
return {std::move(strategies)...};
}
// 使用示例
void demo_metaprogramming_strategy() {
auto normalize = [](int x) { return x / 10; };
auto square = [](int x