PolicyChain - 策略链¶
cf::PolicyChain 是责任链模式(Chain of Responsibility)的一种实现,按优先级顺序执行一系列策略函数。如果某个策略返回了有效值(非 std::nullopt),链就停止;如果返回 std::nullopt,则尝试下一个策略。这种模式特别适合"多种可能的解决方案,优先使用第一个有效的"场景。
为什么需要 PolicyChain¶
很多系统都有"尝试多种方案"的逻辑。比如字体渲染:
- 先尝试系统原生渲染
- 如果不行,尝试 Freetype
- 还不行,回退到内置光栅化器
不用 PolicyChain 的话,代码会是一串嵌套的 if-else:
std::optional<Font> get_font(const std::string& name) {
if (auto f = try_system_font(name)) return f;
if (auto f = try_freetype(name)) return f;
if (auto f = try_builtin(name)) return f;
return std::nullopt;
}
这看起来还好,但当策略数量增加、策略需要动态注册时,硬编码的 if-else 就不够灵活了。PolicyChain 把这些策略变成可组合、可动态管理的链。
基本用法¶
直接构造¶
#include "base/policy_chain/policy_chain.hpp"
cf::PolicyChain<int, const std::string&> chain;
// 添加策略(优先级从高到低)
chain.add_front([](const std::string& s) -> std::optional<int> {
if (!s.empty()) return std::stoi(s);
return std::nullopt;
});
chain.add_back([](const std::string& s) -> std::optional<int> {
return static_cast<int>(s.length()); // 兜底:返回字符串长度
});
// 执行
auto result = chain.execute("42");
if (result) {
std::cout << "Result: " << *result << std::endl; // Result: 42
}
使用工厂函数¶
auto chain = cf::make_policy_chain<int, const std::string&>(
[](const std::string& s) -> std::optional<int> {
if (!s.empty()) return std::stoi(s);
return std::nullopt;
},
[](const std::string& s) -> std::optional<int> {
return static_cast<int>(s.length());
},
[](const std::string&) -> std::optional<int> {
return 0; // 最终兜底
}
);
auto r1 = chain.execute("hello"); // 返回 std::optional(5) — stoi 失败但 length 可用
auto r2 = chain.execute("123"); // 返回 std::optional(123) — stoi 成功
auto r3 = chain.execute(""); // 返回 std::optional(0) — 兜底策略
make_policy_chain 按参数顺序添加策略,第一个参数优先级最高。
使用 Builder 模式¶
auto chain = cf::policy_chain_builder<int, int>()
.then([](int x) -> std::optional<int> {
if (x > 0) return x * 2;
return std::nullopt;
})
.then([](int x) -> std::optional<int> {
if (x < 0) return -x;
return std::nullopt;
})
.then([](int) -> std::optional<int> {
return 0; // 默认值
})
.build();
auto r1 = chain.execute(5); // 10 — 第一个策略处理
auto r2 = chain.execute(-3); // 3 — 第二个策略处理
auto r3 = chain.execute(0); // 0 — 兜底策略
Builder 的 then 按调用顺序添加策略,先添加的优先级高。
核心接口¶
PolicyChain¶
template <typename Ret, typename... Args> class PolicyChain {
// 添加策略到链头(最高优先级)
void add_front(PolicyType policy);
// 添加策略到链尾(最低优先级)
void add_back(PolicyType policy);
// 执行链,返回第一个非 nullopt 的结果
[[nodiscard]] std::optional<Ret> execute(Args... args) const;
// 函数调用语法糖,等价于 execute
[[nodiscard]] std::optional<Ret> operator()(Args... args) const;
// 管理操作
void clear();
[[nodiscard]] bool empty() const;
[[nodiscard]] SizeType size() const;
};
工厂函数和 Builder¶
// 工厂函数:直接创建链
template <typename T, typename... Args, typename... Policies>
auto make_policy_chain(Policies&&... policies);
// Builder 创建器
template <typename T, typename... Args>
auto policy_chain_builder();
执行语义¶
链按策略添加的顺序执行(add_front 的先执行)。每个策略函数签名为 std::optional<Ret>(Args...):
- 返回有效值:链停止,返回该值
- 返回
std::nullopt:继续执行下一个策略 - 所有策略都返回
std::nullopt:整个链返回std::nullopt
典型使用场景¶
平台特性检测¶
auto renderer_chain = cf::make_policy_chain<std::string>(
[]() -> std::optional<std::string> {
if (vulkan_available()) return "Vulkan";
return std::nullopt;
},
[]() -> std::optional<std::string> {
if (opengl_available()) return "OpenGL";
return std::nullopt;
},
[]() -> std::optional<std::string> {
return "Software"; // 兜底
}
);
auto renderer = renderer_chain.execute();
配置值解析¶
auto config_chain = cf::policy_chain_builder<std::string, const std::string&>()
.then([](const std::string& key) -> std::optional<std::string> {
return try_env_variable(key); // 优先环境变量
})
.then([](const std::string& key) -> std::optional<std::string> {
return try_config_file(key); // 其次配置文件
})
.then([](const std::string& key) -> std::optional<std::string> {
return get_default(key); // 最后默认值
})
.build();
资源加载¶
auto loader_chain = cf::make_policy_chain<QImage, const QString&>(
[](const QString& path) -> std::optional<QImage> {
return try_load_from_cache(path);
},
[](const QString& path) -> std::optional<QImage> {
return try_load_from_disk(path);
},
[](const QString& path) -> std::optional<QImage> {
return try_load_from_network(path);
}
);
线程安全¶
PolicyChain 本身不提供线程安全保证。如果在多线程环境下使用:
- 只读场景(构建完成后只调用
execute):安全,因为execute是const方法,内部数据不会被修改。 - 动态修改(运行时调用
add_front/add_back):需要外部同步。
推荐的做法是在初始化阶段构建好链,运行时只调用 execute。
性能考虑¶
- 每个策略通过
std::function存储,有一次间接调用的开销。 - 策略存储在
std::list中,遍历时有缓存不友好的问题。但通常策略数量不多(几个到十几个),影响可以忽略。 execute在最坏情况下会遍历所有策略,时间复杂度 O(n)。
如果策略数量很少(比如 2-3 个)且性能极其敏感,直接用 if-else 可能比 PolicyChain 更快。PolicyChain 的优势在于灵活性和可组合性。