精读C++20设计模式——行为型设计模式:策略模式

精读C++20设计模式------行为型设计模式:策略模式

前言

​ 我们天天都在用策略模式!标准库的算法在设计上就是一种策略模式!

​ 策略模式的核心想法很简单:把算法/行为从使用它的类中抽离出来,封装成可互换的"策略(Strategy)",并允许在运行时或编译期替换这些策略,从而实现算法的可扩展、可测试与解耦。


什么是策略模式?

​ 策略模式把一组可以互换的算法封装到独立对象(或类型)中,客户类(Context)通过统一接口使用它们。注意!策略是"可替换的" ------ Context 不知道具体实现细节,只依赖策略接口。


动态策略

​ 动态策略通常用虚函数(interface + subclass)或类型擦除(std::function / std::unique_ptr 指向多态对象)实现,优点是运行时可替换,灵活适配配置或用户选择;代价是虚调/间接调用开销和更复杂的生命周期管理。

cpp 复制代码
#include <iostream>
#include <memory>
#include <string>

// 策略接口
struct ICompressor {
    virtual ~ICompressor() = default;
    virtual std::string compress(const std::string& data) = 0;
};

// 具体策略 A
struct GzipCompressor : ICompressor {
    std::string compress(const std::string& data) override {
        return "gzip(" + data + ")";
    }
};

// 具体策略 B
struct NoopCompressor : ICompressor {
    std::string compress(const std::string& data) override {
        return data;
    }
};

// Context
class CompressionContext {
public:
    CompressionContext(std::unique_ptr<ICompressor> s) : strategy_(std::move(s)) {}

    void set_strategy(std::unique_ptr<ICompressor> s) { strategy_ = std::move(s); }

    std::string perform(const std::string& data) {
        return strategy_->compress(data);
    }
private:
    std::unique_ptr<ICompressor> strategy_;
};

int main() {
    CompressionContext ctx(std::make_unique<NoopCompressor>());
    std::cout << ctx.perform("hello") << "\n";            // hello

    ctx.set_strategy(std::make_unique<GzipCompressor>());
    std::cout << ctx.perform("hello") << "\n";            // gzip(hello)
}

​ 如您所见,我们实际上包装了两个压缩策略:这是在外面运行的时候配置的------配置、插件或用户交互决定这一切。策略实现复杂且需要继承/多态;需要在程序运行期间动态替换策略。

注意事项

  • 如果策略是有状态的(内部有成员),要明确其共享/独占语义(shared_ptr vs unique_ptr)。
  • 线程安全:多个线程共享策略实例需要保证策略实现是线程安全或每线程独立实例。
  • 单元测试方便:可以替换为 mock 策略。

静态策略

静态策略利用模板参数或 CRTP 把策略在编译期绑定进 Context。它能带来零虚调用、内联优化和更好性能,但牺牲了运行时切换和二进制大小/可重用性的灵活性。

cpp 复制代码
#include <iostream>
#include <string>

// 策略:UpperCaseFormatter / LowerCaseFormatter
struct UpperCaseFormatter {
    static std::string format(std::string s) {
        for (auto &c : s) c = static_cast<char>(std::toupper(c));
        return s;
    }
};

struct LowerCaseFormatter {
    static std::string format(std::string s) {
        for (auto &c : s) c = static_cast<char>(std::tolower(c));
        return s;
    }
};

// Context 模板化
template<typename FormatterPolicy>
class TextProcessor {
public:
    std::string process(const std::string& s) {
        return FormatterPolicy::format(s);
    }
};

int main() {
    TextProcessor<UpperCaseFormatter> up;
    TextProcessor<LowerCaseFormatter> low;

    std::cout << up.process("Hello") << "\n"; // HELLO
    std::cout << low.process("Hello") << "\n"; // hello
}

​ 当然,有时候既想要运行时灵活性又想要简洁接口,可以考虑使用 std::function 或自定义类型擦除(small-buffer optimization),例如:

cpp 复制代码
#include <functional>
#include <iostream>
#include <string>

class Printer {
public:
    using Strategy = std::function<void(const std::string&)>;
    Printer(Strategy s) : strategy_(std::move(s)) {}
    void set_strategy(Strategy s) { strategy_ = std::move(s); }
    void print(const std::string& s) { strategy_(s); }
private:
    Strategy strategy_;
};

int main() {
    Printer p([](const std::string& s){ std::cout << "A: " << s << "\n"; });
    p.print("x");
    p.set_strategy([](const std::string& s){ std::cout << "B: " << s << "\n"; });
    p.print("y");
}

std::function 的优点是写法短、易用,但它也有类型擦除和内存/调用开销。适合策略实现比较小或不在超高频路径的情况。


总结

动态策略(运行时)

  • 优点:高度灵活、运行时可替换、适合插件/配置驱动。
  • 缺点:虚调用 / 间接调用开销、需要处理生命周期/并发。

静态策略(编译期)

  • 优点:零虚开销、编译器优化、内联、类型安全。
  • 缺点:缺乏运行时切换、编译单元膨胀(可能增加二进制),策略必须在编译期已知。

类型擦除(std::function / 自定义)

  • 优点:编码方便、接口统一、比虚继承更轻量(在小策略场景)。
  • 缺点:隐藏类型信息、可能有分配/性能开销。
相关推荐
tedcloud12331 分钟前
cc-switch评测:多AI Coding Agent管理工具详解
数据库·人工智能·sql·学习·自动化
QiLinkOS1 小时前
《打破“用爱发电”:一种基于 Gitee 与时间戳的开源权益分配机制探索》
c语言·数据结构·c++·科技·算法·gitee·开源
Irissgwe1 小时前
c++STL--string类
c++·stl·string
Irissgwe1 小时前
c++类型转换
c++·类型转换·explicit·static_cast·const_cast·dynamic_cast·rtti
胡图图不糊涂^_^1 小时前
测试BUG篇
学习·bug·测试
智者知已应修善业1 小时前
【51单片机用T0定时器方式1,实现0.5S的时间间隔实现第一次一个灯亮、第二次二个灯亮,直到全部灯亮,然后重复整个过程】2023-12-29
c++·经验分享·笔记·算法·51单片机
智者知已应修善业2 小时前
【51单片机4位静态数码管显示1234】2023-11-14
c++·经验分享·笔记·算法·51单片机
抓虾爪2 小时前
ST意法代理商粤科源兴丨LSM6DS3全系列现货库存,LSM6DS3TR-C当天可发
c++
妙为2 小时前
unreal engine5.7.4,创建ThirdPerson第三人称模版,类型是c++崩溃
c++·ue5·虚幻·unreal engine5
郝学胜_神的一滴2 小时前
Qt 高级开发 021:零基础吃透 QVBoxLayout 垂直布局
c++·qt