在c++中,基于策略的设计模式基本上有两种选择:我可以使用单独的类型(基于重载选择),或者指定一个包含所有策略的枚举,并在运行时对它们进行分派。现在写基于策略的设计的首选方式是什么?
演示#include <cstdio>
/* Policy using types */
namespace type_policy
{
struct detached_t{};
struct deferred_t{};
inline constexpr detached_t detached = detached_t{};
inline constexpr deferred_t deferred = deferred_t{};
}
auto do_something(type_policy::detached_t pol)
{
printf("detached type policy selectedn");
}
auto do_something(type_policy::deferred_t pol)
{
printf("deferred type policy selectedn");
}
/* Policy using enums */
enum class enum_policy
{
detached,
deferred,
};
static auto do_something_else(const enum_policy pol)
{
if (pol == enum_policy::deferred) {
printf("deferred enum policy selectedn");
} else {
printf("detached enum policy selectedn");
}
}
int main()
{
do_something(type_policy::deferred);
do_something_else(enum_policy::detached);
}
注意:当枚举分派内置于静态函数中时,编译器也能够在编译时消除条件。它从一开始就不那么啰嗦……这应该是首选的方式吗?
看情况而定。您是否希望让客户(包括您自己)能够轻松地添加自己的新保单?如果是这样,基于类型的设计将允许他们添加新的策略,而不必重新编译现有的代码;他们只需要:
namespace type_policy
{
struct another_t {};
inline constexpr another_t another = another_t{};
}
auto do_something(type_policy::another_t pol)
{
printf("another type policy selectedn");
}
另一方面,基于枚举的设计只能由枚举所有者进行扩展。此外,如果您更改枚举以添加新策略,您将强制客户端重新编译它们的代码,即使它们可能对您刚刚添加的新策略不感兴趣。
话虽如此,基于类型设计的一个缺点是策略最终可能分散在许多文件中,而基于枚举的策略则在枚举类中很好地组合在一起。
基于类型设计的另一个缺点是与运行时值一起使用稍微困难一些(因为您需要知道在do_something
中传递的确切类型),与基于枚举的相比,在基于枚举的情况下,简单地从int转换为enum_policy
就可以了。
一般来说,枚举是表达多个选项中的一个的自然方式,而不是使用更难以理解的类似trait的机制,并且迫使您为任何新选项生成新的重载。
关于最后一点,使用枚举将通过更小、更清晰的界面简化对可用策略的更改。任何基于策略的函数(do_something_else
)将保持一个单一的过载,而不是破坏API兼容性。
最后,从c++ 11开始,与可以从任何整数隐式强制转换的C类枚举相比,枚举类的使用增加了它的安全性。