策略模式
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 95 课,共 104 课。
策略模式定义了一组算法,将每种算法封装起来,并使它们可以互换。这样,你就可以在运行时更改对象的行为,而无需修改它的代码。算法可以独立于使用它们的客户端进行变化。
该模式由三个部分组成:声明算法方法的策略接口、实现不同变体的具体策略,以及使用策略的上下文:
#include <iostream>
#include <memory>
// 策略接口
class PaymentStrategy {
public:
virtual void pay(int amount) = 0;
virtual ~PaymentStrategy() = default;
};
// 具体策略
class CreditCardPayment : public PaymentStrategy {
public:
void pay(int amount) override {
std::cout << "Paid " << amount << " via Credit Card\n";
}
};
class PayPalPayment : public PaymentStrategy {
public:
void pay(int amount) override {
std::cout << "Paid " << amount << " via PayPal\n";
}
};
// 上下文
class ShoppingCart {
std::unique_ptr<PaymentStrategy> strategy;
public:
void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
strategy = std::move(s);
}
void checkout(int total) {
if (strategy) strategy->pay(total);
}
};
int main() {
ShoppingCart cart;
cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
cart.checkout(100);
cart.setPaymentMethod(std::make_unique<PayPalPayment>());
cart.checkout(50);
}ShoppingCart 不知道自己使用的是哪种支付方式,它只会对当前设置的策略调用 pay()。你可以在运行时通过 setPaymentMethod() 切换策略,让系统更灵活,也更容易扩展新的支付方式。
当某项任务有多种算法,且你想动态切换算法,或者想避免使用条件语句来选择行为时,可以使用 Strategy。
挑战
简单让我们构建一个使用策略模式计算配送费用的配送计算器,费用取决于不同的配送方式。这是一个需要在运行时切换算法的实用场景:同一个包裹可以通过陆运、空运或特快配送,每种方式都有自己的定价逻辑。
你需要将代码组织到三个文件中:
ShippingStrategy.h:定义策略接口和具体的配送策略。创建一个抽象的
ShippingStrategy类,其中包含纯虚方法calculateCost(double weight),该方法以 double 类型返回配送费用,并包含一个虚析构函数。然后实现三个具体策略:
GroundShipping:每单位重量收费1.5(weight * 1.5)AirShipping:每单位重量收费4.0(weight * 4.0)ExpressShipping:每单位重量收费6.5,另加固定费用10.0(weight * 6.5 + 10.0)
ShippingService.h:创建使用配送策略的上下文类。你的
ShippingService类应该将std::unique_ptr<ShippingStrategy>作为私有成员。实现以下方法:- 使用
setStrategy(std::unique_ptr<ShippingStrategy> strategy)方法更改配送方式 - 使用
calculateShipping(double weight)方法,通过当前策略计算并返回费用
如果调用
calculateShipping时没有设置策略,则返回0.0。- 使用
main.cpp:演示如何在运行时切换策略。读取两个输入值:
- 包裹重量(double)
- 配送方式:
ground、air或express
创建一个
ShippingService,并根据输入的配送方式设置相应的策略。计算并打印配送费用。然后切换到另一种策略(如果输入不是
air,就使用air,否则使用ground),并用相同的重量再次计算费用。这展示了在运行时切换策略的优势。每行打印一项费用,金额必须精确到小数点后一位,并在前面加上配送方式名称:
[Method]: $[cost]
例如,输入 5.0 和 ground 时:
Ground: $7.5
Air: $20.0输入 3.0 和 express 时:
Express: $29.5
Air: $12.0输入 10.0 和 air 时:
Air: $40.0
Ground: $15.0请注意,ShippingService 不需要了解每种定价算法的具体细节。它只需将计算委托给当前设置的策略。你可以轻松添加新的配送方式(例如无人机配送或当日送达),而完全不必修改服务类。
亲手试试
#include <iostream>
#include <string>
#include <iomanip>
#include <memory>
#include "ShippingStrategy.h"
#include "ShippingService.h"
int main() {
double weight;
std::string method;
std::cin >> weight;
std::cin >> method;
// 将输出设置为保留 1 位小数
std::cout << std::fixed << std::setprecision(1);
ShippingService service;
// TODO:根据输入的配送方式("ground"、"air" 或 "express"):
// 1. 为 service 设置合适的策略
// 2. 计算并按以下格式输出费用:"[Method]: $[cost]"
// TODO:切换到另一种策略:
// 如果输入是 "air",切换到 GroundShipping
// 否则,切换到 AirShipping
// 计算并输出新的费用
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器