一、核心概念

1.1 定义

策略模式(Strategy Pattern)核心思想是将算法家族封装起来,使它们之间可以相互替换,且算法的变化不会影响使用算法的客户端。该模式通过面向对象的多态机制,实现了算法与使用环境的解耦,让代码结构更清晰、可维护性更强。

1.2 核心解决的问题

在传统开发中,若一个功能存在多种实现算法(如排序算法、支付方式、日志记录方式),通常会使用if-else或switch语句进行分支判断,选择不同的算法实现。这种方式存在以下问题:

  • 代码耦合度高:算法逻辑与调用逻辑混杂在同一代码块中

  • 扩展性差:新增算法需修改原有判断逻辑,违反开闭原则

  • 维护成本高:算法逻辑分散,后续修改易引发连锁反应

  • 可读性差:大量分支判断导致代码逻辑复杂,难以理解

策略模式通过将不同算法封装为独立的策略类,彻底解决了上述问题,使代码结构更符合面向对象设计原则。

二、结构组成

策略模式包含三个核心角色,各角色职责明确,协同工作实现算法的灵活切换:

2.1 抽象策略类(Strategy)

  • 职责:定义所有具体策略类的公共接口,声明算法的核心方法

  • 形式:通常以纯虚基类(抽象类)实现,确保所有具体策略类遵循统一的接口规范

  • 作用:为客户端提供统一的调用入口,屏蔽不同算法的实现差异

2.2 具体策略类(ConcreteStrategy)

  • 职责:实现抽象策略类中定义的接口,提供具体的算法实现

  • 形式:继承自抽象策略类,重写抽象方法,封装特定算法的完整逻辑

  • 特点:可根据需求灵活新增,无需修改现有代码,符合开闭原则

2.3 上下文类(Context)

  • 职责:维护一个对抽象策略类的引用,为客户端提供使用策略的接口

  • 形式:包含策略对象的成员变量,提供设置策略(切换算法)和执行策略(调用算法)的方法

  • 作用:隔离客户端与具体策略类,客户端只需通过上下文类即可使用不同策略,无需直接与具体策略交互

三、实现示例

以下以 "支付系统" 为例,实现策略模式的完整代码。支付系统中存在多种支付方式(支付宝、微信支付、银联支付),每种支付方式对应一种策略。

3.1 抽象策略类(PaymentStrategy)

1
2
3
4
5
6
7
8
9
10
#include <string>
// 抽象支付策略类:定义支付算法的公共接口
class PaymentStrategy {
public:
// 纯虚函数:声明支付方法,参数为支付金额和订单号
virtual bool pay(double amount, const std::string& orderId) = 0;

// 虚析构函数:确保子类对象能正确析构,避免内存泄漏
virtual ~PaymentStrategy() {}
};

3.2 具体策略类(ConcreteStrategy)

3.2.1 支付宝支付策略(AlipayStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
// 支付宝支付策略:实现具体的支付宝支付逻辑
class AlipayStrategy : public PaymentStrategy {
public:
// 重写支付方法,实现支付宝支付逻辑
bool pay(double amount, const std::string& orderId) override {
// 模拟支付宝支付流程:参数校验 -> 调用支付宝接口 -> 返回支付结果
if (amount <= 0) {
std::cerr << "支付宝支付失败:订单金额无效(订单号:" << orderId << ")" << std::endl;
return false;
}

// 模拟支付成功的业务逻辑(如记录支付日志、更新订单状态)
std::cout << "支付宝支付成功!订单号:" << orderId
<< ",支付金额:" << amount << "元" << std::endl;
return true;
}
};

3.2.2 微信支付策略(WechatPayStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 微信支付策略:实现具体的微信支付逻辑
class WechatPayStrategy : public PaymentStrategy {
public:
// 重写支付方法,实现微信支付逻辑
bool pay(double amount, const std::string& orderId) override {
if (amount <= 0) {
std::cerr << "微信支付失败:订单金额无效(订单号:" << orderId << ")" << std::endl;
return false;
}

std::cout << "微信支付成功!订单号:" << orderId
<< ",支付金额:" << amount << "元" << std::endl;
return true;
}
};

3.2.3 银联支付策略(UnionPayStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 银联支付策略:实现具体的银联支付逻辑
class UnionPayStrategy : public PaymentStrategy {
public:
// 重写支付方法,实现银联支付逻辑
bool pay(double amount, const std::string& orderId) override {
if (amount <= 0) {
std::cerr << "银联支付失败:订单金额无效(订单号:" << orderId << ")" << std::endl;
return false;
}

std::cout << "银联支付成功!订单号:" << orderId
<< ",支付金额:" << amount << "元" << std::endl;
return true;
}
};

3.3 上下文类(PaymentContext)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 支付上下文类:维护策略对象,为客户端提供支付接口
class PaymentContext {
private:
// 持有抽象策略类的指针,实现多态调用
PaymentStrategy* m_strategy;

public:
// 构造函数:初始化时指定默认支付策略
explicit PaymentContext(PaymentStrategy* strategy) : m_strategy(strategy) {}

// 析构函数:释放策略对象内存(若由上下文负责管理)
~PaymentContext() {
if (m_strategy != nullptr) {
delete m_strategy;
m_strategy = nullptr;
}
}

// 切换策略:动态更换支付方式,无需修改其他代码
void setStrategy(PaymentStrategy* strategy) {
// 释放原有策略对象
if (m_strategy != nullptr) {
delete m_strategy;
}
// 设置新策略对象
m_strategy = strategy;
}

// 执行支付:调用当前策略的支付方法,屏蔽具体实现细节
bool executePayment(double amount, const std::string& orderId) {
if (m_strategy == nullptr) {
std::cerr << "支付失败:未设置支付策略" << std::endl;
return false;
}
// 多态调用:根据当前策略对象的实际类型,调用对应的pay方法
return m_strategy->pay(amount, orderId);
}
};

3.4 客户端调用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
// 1. 创建上下文对象,并指定默认支付策略(支付宝)
PaymentContext context(new AlipayStrategy());

// 2. 使用支付宝支付订单1
context.executePayment(99.9, "ORDER_20250827_001");

// 3. 切换支付策略为微信支付,支付订单2
context.setStrategy(new WechatPayStrategy());
context.executePayment(199.5, "ORDER_20250827_002");

// 4. 切换支付策略为银联支付,支付订单3
context.setStrategy(new UnionPayStrategy());
context.executePayment(299.0, "ORDER_20250827_003");

return 0;
}

3.5 代码运行结果

1
2
3
支付宝支付成功!订单号:ORDER_20250827_001,支付金额:99.9元
微信支付成功!订单号:ORDER_20250827_002,支付金额:199.5元
银联支付成功!订单号:ORDER_20250827_003,支付金额:299元

四、典型应用场景

策略模式在实际开发中应用广泛,以下是常见的适用场景:

4.1 多种算法实现同一功能的场景

  • 排序算法:快速排序、归并排序、冒泡排序等,可封装为不同策略,根据数据规模动态选择

  • 数据压缩算法:ZIP、GZIP、BZIP2 等,根据压缩率和速度需求切换策略

  • 加密算法:AES、RSA、DES 等,根据安全性和性能需求选择不同策略

4.2 避免大量分支判断的场景

  • 支付系统:如上文示例,多种支付方式(支付宝、微信、银联)无需if-else判断

  • 日志系统:控制台日志、文件日志、数据库日志,根据环境动态切换

  • 报表生成:PDF 报表、Excel 报表、HTML 报表,根据用户需求选择生成策略

4.3 算法需要动态切换的场景

  • 游戏开发:角色的移动策略(步行、跑步、飞行),根据游戏状态实时切换

  • UI 主题切换:浅色主题、深色主题、自定义主题,用户可动态切换显示策略

  • 缓存策略:LRU(最近最少使用)、FIFO(先进先出)、LFU(最不经常使用),根据业务场景切换缓存淘汰策略

五、实现技巧与最佳实践

5.1 策略对象的创建与管理

  • 内存管理:若上下文类负责策略对象的创建,需在析构函数中正确释放内存,避免内存泄漏;也可使用智能指针(std::unique_ptr、std::shared_ptr)自动管理内存,简化代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使用智能指针管理策略对象,无需手动释放内存
#include <memory>
class PaymentContext {
private:
std::unique_ptr<PaymentStrategy> m_strategy;
public:
explicit PaymentContext(std::unique_ptr<PaymentStrategy> strategy)
: m_strategy(std::move(strategy)) {}

void setStrategy(std::unique_ptr<PaymentStrategy> strategy) {
m_strategy = std::move(strategy);
}
// ...其他方法
};

// 客户端调用
PaymentContext context(std::make_unique<AlipayStrategy>());
  • 策略对象复用:若策略对象无状态(不包含成员变量或成员变量仅为配置信息),可创建单例策略对象,避免频繁创建和销毁,提升性能

5.2 结合其他设计模式使用

  • 与工厂模式结合:当策略类较多时,可通过策略工厂类统一创建策略对象,降低客户端与具体策略类的耦合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 策略工厂类:根据支付类型创建对应的策略对象
class PaymentStrategyFactory {
public:
static std::unique_ptr<PaymentStrategy> createStrategy(const std::string& payType) {
if (payType == "alipay") {
return std::make_unique<AlipayStrategy>();
} else if (payType == "wechat") {
return std::make_unique<WechatPayStrategy>();
} else if (payType == "unionpay") {
return std::make_unique<UnionPayStrategy>();
}
return nullptr;
}
};

// 客户端调用:通过工厂创建策略,无需直接new具体策略类
auto strategy = PaymentStrategyFactory::createStrategy("alipay");
PaymentContext context(std::move(strategy));
  • 与享元模式结合:对于有状态但状态可共享的策略对象,可通过享元模式缓存策略对象,减少内存占用

5.3 注意事项

  • 避免过度设计:若算法数量少且稳定(不会新增或修改),使用简单的if-else可能更简洁,无需强行使用策略模式

  • 接口一致性:抽象策略类的接口设计需合理,确保所有具体策略类都能通过统一接口实现功能,避免接口过大或过小

  • 线程安全:若策略对象被多线程共享,需确保策略类的方法是线程安全的,或通过线程局部存储(TLS)避免线程安全问题

  • 策略选择逻辑:策略的选择逻辑应放在客户端或专门的策略选择器中,上下文类仅负责执行策略,不负责策略选择,保持职责单一

六、策略模式的优缺点总结

6.1 优点

符合开闭原则:新增策略只需添加新的具体策略类,无需修改现有代码

降低耦合度:算法与调用逻辑分离,客户端无需了解算法的具体实现

避免分支判断:消除大量if-else或switch语句,代码更简洁、可读性更强

提高可测试性:每个策略类可独立测试,测试用例更简单

算法灵活切换:可在运行时动态切换策略,适应不同业务场景

6.2 缺点

类数量增加:每种算法对应一个策略类,若算法数量多,会导致类数量激增

客户端需了解策略差异:客户端需知道不同策略的区别,才能选择合适的策略

策略类需暴露接口:抽象策略类的接口设计需提前规划,若接口变更,所有具体策略类都需修改