Menu
CoddyTech

策略模式

本课属于 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。

challenge icon

挑战

简单

让我们构建一个使用策略模式计算配送费用的配送计算器,费用取决于不同的配送方式。这是一个需要在运行时切换算法的实用场景:同一个包裹可以通过陆运、空运或特快配送,每种方式都有自己的定价逻辑。

你需要将代码组织到三个文件中:

  • 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:演示如何在运行时切换策略。

    读取两个输入值:

    1. 包裹重量(double)
    2. 配送方式: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;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C++ 在线编译器