1.概述
策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
策略模式通常应用于需要多种算法进行操作的场景,如排序、搜索、数据压缩等。在这些情况下,不同的算法有不同的优缺点和适用性,因此需要进行选择。
通过使用策略模式,我们可以轻松地切换算法,而无需修改客户端代码。这使得代码更加灵活、可扩展、易于维护,减少了重复的代码,并提高了代码的可读性。
2.结构
策略模式的结构包括以下几个部分:
抽象策略(Strategy)类: 定义所有支持的算法或行为的公共接口或抽象类。
具体策略(Concrete Strategy)类: 实现抽象策略接口,提供具体的算法或行为。
环境(Context)类: 持有一个对抽象策略的引用,并且通过该引用调用具体策略类中实现的算法或行为。
在策略模式中,客户端代码仅与环境类及其抽象策略接口交互,无需关心具体实现细节。当需要更改算法或行为时,只需要修改具体策略类即可,而无需修改客户端代码或其他策略类。
3.实现
3.1 实例类比
假如你需要前往机场。 你可以选择乘坐公共汽车、 预约出租车或骑自行车。 这些就是你的出行策略。 你可以根据预算或时间等因素来选择其中一种策略。
3.2 实例引入
在支付系统中,我们就可以使用策略模式,针对不同的支付方式封装成不同的策略类。每个策略类负责实现一个特定的支付方式,并提供相应的算法来处理付款。这样,客户端代码就可以通过选择不同的策略类来实现不同的支付方式,而无需了解每种支付方式的具体实现细节。
例如,假设我们使用xx宝,可用银行卡、微信支付和支付宝支付三种支付方式。每种支付方式都有自己的实现方式和规则,但客户端并不需要知道支付方式的具体实现,只需要选择一个支付策略即可。这时候,我们可以使用策略模式来实现支付系统,将每种支付方式封装成一个支付策略类,让客户端根据需要选择不同的支付策略类。
3.3 结构分析
在这个案例中,代码中的各个结构可以对应到策略模式中的不同角色:
- Context(上下文):对应代码中的PaymentContext类,负责调用具体支付策略对象的付款方法。
- Strategy(策略):对应代码中的PaymentStrategy接口,定义了所有支持的支付方式的公共接口。
- ConcreteStrategy(具体策略):对应代码中的BankCardStrategy、WeChatPayStrategy和AliPayStrategy类,实现了PaymentStrategy接口,提供了不同的支付方式实现逻辑。
类图如下:
3.4 具体实现
cpp
#include <iostream>
#include <string>
using namespace std;
//抽象策略
class PaymentStrategy {
public:
virtual void pay(double money) = 0;
};
//具体策略
/**
* 银联支付
*/
class BankCardStrategy :public PaymentStrategy {
private:
/** 银行卡卡号 */
string cardNumber;
/** 银行卡有效期 */
string expiryDate;
public:
BankCardStrategy(string cardNumber, string expiryDate) {
this->cardNumber = cardNumber;
this->expiryDate = expiryDate;
}
void pay(double money) {
// 基于卡的付款逻辑
std::cout << "使用银联支付:"<< money <<"元" << endl;
}
};
/**
* 微信
*/
class WeChatPayStrategy :public PaymentStrategy {
private:
/** 微信openId */
string openId;
public:
WeChatPayStrategy(string openId) {
this->openId = openId;
}
void pay(double money) {
// 基于微信支付的付款逻辑
std::cout << "使用微信支付:" << money << "元" << endl;
}
};
/**
* 支付宝
*/
class AliPayStrategy :public PaymentStrategy {
private:
string account;
string password;
public:
AliPayStrategy(string account, string password) {
this->account = account;
this->password = password;
}
void pay(double money) {
// 基于支付宝的付款逻辑
std::cout << "使用支付宝支付:" <<money << "元" << endl;
}
};
class PaymentContext {
private:
PaymentStrategy *_paymentStrategy;
public:
void setPaymentStrategy(PaymentStrategy* paymentStrategy) {
_paymentStrategy = paymentStrategy;
}
void pay(double money) {
if (_paymentStrategy == nullptr) {
throw std::runtime_error("支付策略不能为空");
}
_paymentStrategy->pay(money);
}
};
int main()
{
std::cout << "策略模式........" << endl;
// 创建一个银联支付策略对象
BankCardStrategy *bankCardStrategy = new BankCardStrategy("014600002206", "20224-12");
// 创建一个微信支付策略对象
WeChatPayStrategy *weChatPayStrategy = new WeChatPayStrategy("GoWJW");
// 创建一个支付宝支付策略对象
AliPayStrategy *aliPayStrategy = new AliPayStrategy("alipayaccount@gowjw.com", "alipaypassword");
// 创建一个支付上下文对象
PaymentContext *paymentContext = new PaymentContext();
// 使用银联支付
paymentContext->setPaymentStrategy(bankCardStrategy);
paymentContext->pay(2024.4);
// 使用微信支付
paymentContext->setPaymentStrategy(weChatPayStrategy);
paymentContext->pay(2024.4);
// 使用支付宝支付
paymentContext->setPaymentStrategy(aliPayStrategy);
paymentContext->pay(2024.4);
return 0;
}
3.5 运行结果
4.策略优缺点
策略模式主要优点和缺点如下:
优点:
- 策略模式使得各种算法可以在不修改原有代码的情况下替换或者新增,提高了代码的可扩展性和可维护性;
- 策略模式将算法的实现从上下文中解耦出来,使得算法可以独立进行单元测试;
- 策略模式符合开闭原则,即对扩展开放,对修改关闭,可以通过增加新的策略类来扩展应用,而无需修改原有代码。
缺点:
- 如果策略数量过多,会导致类数量增加,增加系统的复杂度;
- 客户端需要知道所有的策略类,并选择合适的策略类,这可能会导致客户端代码较为复杂;
- 策略模式将算法的实现从上下文中解耦出来,同时也意味着上下文不能控制策略的执行顺序,需要客户端自行控制执行顺序。
5.应用场景
- 系统需要动态地在几种算法中选择一种,或者根据不同的条件选择不同的算法;
- 系统中有许多类似的行为,但是具体实现上有所不同,可以使用策略模式将这些行为抽象出来,并定义一个接口或抽象类,然后由具体的实现类来实现这个接口或抽象类;
- 一些算法使用了相同的数据,但是实现上有所不同,可以使用策略模式来避免代码重复和代码膨胀,节省代码维护成本。