设计模式可以分为行为型、创建型、结构型,具体包含的设计模式如下:
行为型(容易上手且收益高)
策略模式(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;
}