designPattern

54 阅读6分钟

设计模式可以分为行为型创建型结构型,具体包含的设计模式如下:

行为型(容易上手且收益高)
策略模式(Strategy)
观察者模式(Observer)
命令模式(Command)
责任链(Chain of Responsibility)
状态(State)
迭代器(Iterator)
备忘录(Memento)
访问者(Visitor)
中介者(Mediator)
解释器(Interpreter)
创建型
工厂方法(Factory Method)
抽象工厂(Abstract Factory)
建造者(Builder)
单例(Singleton)
原型(Prototype)
结构型
适配器(Adapter)
装饰器(Decorator)
代理(Proxy)
外观(Facade)
组合(Composite)
享元(Flyweight)
桥接(Bridge)
策略模式

意图:将一组可互换的算法封装成策略对象,运行时选择;对调用方透明。 适用:算法可替换、想消除条件分支、希望扩展新算法而不改动调用方。 要点:定义统一策略接口;Context 持有策略;支持运行时切换;可用模板做“静态策略”。

练习 1(易):支付方式可切换(信用卡/PayPal)

目标:熟悉接口 + 运行时策略切换。 关键点:接口抽象、组合而非继承、消除 if-else 链。

建议练习

新增 ApplePay/WeChatPay 策略,不改 Checkout。 把策略配置从命令行或环境变量读取,实现运行时选择。

朴素版本:

#include <iostream>
#include <string>

enum class PaymentMethod {CreditCard, Paypal};

class Checkout {
public:
    // 为了示例简单,把“支付账户信息”直接塞进来
    void setCreditCard(std::string num) { cardNumber = std::move(num); }
    void setPayPal(std::string mail) { paypalEmail = std::move(mail); }

    void pay(double amount, PaymentMethod method) {
        // 缺点:每增加一种支付方式,都要改这里(违反开闭原则)
        if (method == PaymentMethod::CreditCard) {
            if (cardNumber.empty()) { std::cout << "No card set. \n"; return; }
            std::cout << "[CreditCard] charge " << amount << " to card " << cardNumber << "\n";
        } else if (method == PaymentMethod::Paypal) {
            if (paypalEmail.empty()) { std::cout << "No PayPal set.\n"; return; }
            std::cout << "[Paypal] charge "<< amount << " to account " << paypalEmail << "\n";
        } else {
            std::cout << "Unknown method.\n";
        }
    }

private:
    std::string cardNumber;
    std::string paypalEmail;
};

int main() {
    Checkout c;
    c.setCreditCard("4111-1111-1111-1111");
    c.setPayPal("user@example.com");

    c.pay(99.9, PaymentMethod::CreditCard);
    c.pay(149.0, PaymentMethod::Paypal);
    return 0;
}

使用策略模式的版本:

#include <iostream>
#include <memory>
#include <string>

struct IPaymentStrategy {
    virtual ~IPaymentStrategy() = default;
    virtual void pay(double amount) = 0;
};

struct CreditCardStrategy : IPaymentStrategy {
    std::string cardNumber;
    explicit CreditCardStrategy(std::string num) : cardNumber(std::move(num)) {}
    void pay(double amount) override {
        std::cout << "[CreditCard] charge " << amount << " to card " << cardNumber << "\n";
    }
};

struct PayPalStrategy : IPaymentStrategy {
    std::string email;
    explicit PayPalStrategy(std::string mail) : email(std::move(mail)) {}
    void pay(double amount) override {
        std::cout << "[PayPal] charge " << amount << " to account " << email << "\n";
    }
};

struct ApplePay : IPaymentStrategy {
    int phoneNum;
    explicit ApplePay(int num) : phoneNum(num) {}
    void pay(double amount) override {
        std::cout << "[ApplePay] charge " << amount << " to account " << phoneNum << "\n";
    }
};

struct WeChatPay : IPaymentStrategy {
    std::string wxNum;
    explicit WeChatPay(std::string num) : wxNum(num) {}
    void pay(double amount) override {
        std::cout << "[WeChatPay] charge " << amount << " to account " << wxNum << "\n";
    }
};

class Checkout {
public:
    void setStrategy(std::unique_ptr<IPaymentStrategy> s) { strategy = std::move(s); }
    void pay(double amount) {
        if (!strategy) { std::cout << "No payment strategy set.\n"; return; }
        strategy->pay(amount);
    }
private:
    std::unique_ptr<IPaymentStrategy> strategy;
};

int main() {
    Checkout c;
    c.setStrategy(std::make_unique<CreditCardStrategy>("4111-1111-1111-1111"));
    c.pay(99.9);

    c.setStrategy(std::make_unique<PayPalStrategy>("user@example.com"));
    c.pay(149.0);

    c.setStrategy(std::make_unique<ApplePay>(10086));
    c.pay(80.0);

    c.setStrategy(std::make_unique<WeChatPay>("testAccount"));
    c.pay(1111.2);

    return 0;
}

练习 2(中):图片压缩策略(PNG/JPEG)

目标:更贴近业务的策略切换;从配置/扩展点选择策略。 关键点:通过工厂函数/映射从字符串选择策略实现;便于扩展第三方算法。

建议练习

用配置文件或 JSON 选择策略与参数(quality)。 用动态库注册新策略(面向插件扩展)。

朴素版本:

#include <iostream>
#include <string>
#include <algorithm>
#include <stdexcept>

// 朴素写法:把所有算法分支集中在一个类里用if-else / switch分发
// 缺点:每新增/修改一种算法,都要改这个类(违反开闭原则)
//      难以做插件化扩展:单元测试粒度粗(难以独立测试某个算法实现)

class CompressionService {
public:
    // algo: "png" | "jpeg" | "jpg"
    // quality: 仅在 JPEG/JPG时生效(0-100),其他算法忽略
    void compress(const std::string& in,
                  const std::string& out,
                  std::string algo,
                  int quality = 85) {
        toLower(algo);

        if (algo == "png") {
            // 这里本应调用真实 PNG 压缩库(如 libpng),示例里仅打印
            std::cout << "[PNG] compress " << in << " -> " << out
                      << " (lossless, default settings)\n";
            // ... 真实实现:读文件 -> 编码 -> 写文件
        } else if (algo == "jpeg" || algo == "jpg") {
            if (quality < 0) quality = 0;
            if (quality > 100) quality = 100;
            // 这里本应调用 libjpeg-turbo 等库
            std::cout << "[JPEG] compress " << in << " -> " << out
                      << " (quality=" << quality << ")\n";
            // ... 真实实现:读文件 -> 颜色空间转换 -> DCT/量化 -> 写文件
        } else {
            std::cerr << "Unknown algorithm: " << algo << "\n";
        }
    }
private:
    static void toLower(std::string& s) {
        std::transform(s.begin(), s.end(), s.begin(),
                       [](unsigned char c) {return static_cast<char>(std::tolower(c));});
    }
};

int main(int argc, char** argv) {
    // 用法:
    // ./app [algo] [quality]
    // 示例:
    // ./app png
    // ./app jpeg 90
    std:: string algo = (argc > 1 ? argv[1] : "png");
    int quality = 85;
    if (argc > 2) {
        try {
            quality = std::stoi(argv[2]);
        } catch (const std::exception&) {
            std::cerr << "Invalid quality, fallback to 85.\n";
            quality = 85;
        }
    }

    CompressionService svc;
    svc.compress("input_image.png", "output.bin", algo, quality);
    return 0;
}

使用策略模式版本:

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <functional>

struct ICompressor {
    virtual ~ICompressor() = default;
    virtual void compress(const std::string& in, const std::string& out) = 0;
};

struct PngCompressor : ICompressor {
    void compress(const std::string& in, const std::string& out) override {
        std::cout << "[PNG] compress "<< in << " -> " << out << " (lossless)\n";
    }
};

struct JpegCompressor : ICompressor {
    int quality{85};
    explicit JpegCompressor(int q = 85) : quality(q) {}
    void compress(const std::string& in, const std::string& out) override {
        std::cout << "[JPEG] compress " << in << " -> " << out << " (quality=" << quality << ")\n";
    }
};

class CompressionContext {
public:
    void set(std::unique_ptr<ICompressor> c) { algo = std::move(c); }
    void run(const std::string& in, const std::string& out) {
        if (!algo) { std::cout << "No compressor.\n"; return; }
        algo->compress(in, out);
    }
private:
    std::unique_ptr<ICompressor> algo;
};

std::unique_ptr<ICompressor> makeCompressor(const std::string& name) {
    static const std::unordered_map<std::string, std::function<std::unique_ptr<ICompressor>()>> registry = {
            {"png", [] { return std::make_unique<PngCompressor>(); }},
            {"jpeg", [] { return std::make_unique<JpegCompressor>(90); }},
            {"jpg", [] { return std::make_unique<JpegCompressor>(80); }},
    };
    if (auto it = registry.find(name); it != registry.end()) return it->second();
    return {};
}

int main(int argc, char** argv) {
    std::string algo = (argc > 1 ? argv[1] : "png");
    auto c = makeCompressor(algo);
    CompressionContext ctx;
    ctx.set(std::move(c));
    ctx.run("input.png", "out.bin");
    return 0;
}

练习 3(难):静态策略(模板策略)与运行时策略并存

目标:理解“策略”不一定要虚函数;编译期选择可消除虚调用开销(Policy-based design)。 关键点:模板策略作为类型参数;同时保留运行时策略以满足动态需求。

建议练习

用 CRTP 或概念(concepts)约束静态策略接口。 在热路径/数值密集型算法中替代虚调用,观察性能差异(-O2/-O3/ -fno-rtti 对比)。

朴素版本:

#include <iostream>
#include <string>

enum class DiscountKind { None, Percentage };

// 朴素运行时版本:用枚举 + 分支
class CartNaiveRT {
public:
    CartNaiveRT(DiscountKind kind, int percent = 0)
        : kind_(kind), percent_(percent) {
        if (percent_ < 0) percent_ = 0;
        if (percent_ > 100) percent_ = 100;
    }

    double checkout(double base) const {
        switch (kind_) {
            case DiscountKind::None:
                return base;
            case DiscountKind::Percentage:
                return base * (1.0 - static_cast<double>(percent_) / 100.0);
            default:
                return base;
        }
    }

    void setKind(DiscountKind k) { kind_ = k; }
    void setPercent(int p) {
        percent_ = (p < 0 ? 0 : (p > 100 ? 100 : p));
    }

private:
    DiscountKind kind_;
    int percent_; // 0..100
};

// 朴素“静态选择”版本:模版参数控制分支(无策略类、无虚函数)
// 注意:为兼容C++20的NTTP限制,这里用 int 表示折扣百分比(0 .. 100)
template <DiscountKind K, int Percent = 0>
class CartNaiveStatic {
public:
    double checkout(double base) const {
        if constexpr (K == DiscountKind::None) {
            return base;
        } else { // K == Percentage
            static_assert(Percent >= 0 && Percent <= 100, "Percent must be 0..100");
            return base * (1.0 - static_cast<double>(Percent) / 100.0);
        }
    }
};

int main() {
    std::cout << "== Runtime naive ==\n";
    CartNaiveRT crt1(DiscountKind::None);
    std::cout << "No discount: " << crt1.checkout(100.0) << "\n";

    CartNaiveRT crt2(DiscountKind::Percentage, 20);
    std::cout << "20% off: " << crt2.checkout(100.0) << "\n";

    // 动态切换
    crt2.setKind(DiscountKind::None);
    std::cout << "Switch to none: " << crt2.checkout(100.0) << "\n";
    crt2.setKind(DiscountKind::Percentage);
    crt2.setPercent(35);
    std::cout << "Switch to 35% " << crt2.checkout(100.0) << "\n";

    std::cout << "\n== Static naive ==\n";
    CartNaiveStatic<DiscountKind::None> cs1;
    std::cout << "Static none: " << cs1.checkout(100.0) << "\n";

    CartNaiveStatic<DiscountKind::Percentage, 20> cs2;
    std::cout << "Static 20% " << cs2.checkout(100.0) << "\n";
    return 0;
}

策略模式版本:

#include <iostream>
#include <memory>
#include <string>
#include <utility>

// 运行时策略接口
struct IPricing {
    virtual ~IPricing() = default;
    virtual double price(double base) const = 0;
};

struct NoDiscountRT : IPricing {
    double price(double base) const override { return base; }
};

struct PercentageDiscountRT : IPricing {
    double pct; explicit PercentageDiscountRT(double p) : pct(p) {}
    double price(double base) const override { return base * (1.0 - pct); }
};

// 静态策略(无需虚函数开销)
struct NoDiscount {
    double price(double base) const { return base; }
};

struct PercentageDiscount {
    double pct; explicit PercentageDiscount(double p) : pct(p) {}
    double price(double base) const { return base * (1.0 - pct); }
};

// Context A: 运行时策略
class CartRt {
public:
    explicit CartRt(std::unique_ptr<IPricing> p) :policy(std::move(p)) {}
    double checkout(double base) const { return policy ? policy->price(base) : base; }
private:
    std::unique_ptr<IPricing> policy;
};

// Context B: 静态策略(策略作为类型+对象)
template <typename Policy>
class CartStatic {
public:
    explicit CartStatic(Policy p) : policy(std::move(p)) {}
    double checkout(double base) const { return policy.price(base); }
private:
    Policy policy;
};

int main() {
    // 运行时:可动态切换
    CartRt cartRT1(std::make_unique<NoDiscountRT>());
    std::cout << "RT no discount: " <<cartRT1.checkout(100) << "\n";
    CartRt cartRT2(std::make_unique<PercentageDiscountRT>(0.2));
    std::cout << "RT 20% off: " << cartRT2.checkout(100) << "\n";

    // 静态:零需调用开销(编译期内联)
    CartStatic<NoDiscount> cartS1(NoDiscount{});
    std::cout << "Static no discount: " << cartS1.checkout(100) << "\n";
    CartStatic<PercentageDiscount> cartS2(PercentageDiscount{0.2});
    std::cout << "Static 20% off: " << cartS2.checkout(100) << "\n";

    return 0;
}