在来自wikipedia的基于策略的设计的hello world示例中,我们使用一个通用接口HelloWorld,并通过模板使用不同的策略对其进行配置-到目前为止还不错:
int main() {
// Example 1
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>
HelloWorldEnglish;
HelloWorldEnglish hello_world;
hello_world.Run(); // Prints "Hello, World!".
// Example 2
// Does the same, but uses another language policy.
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>
HelloWorldGerman;
HelloWorldGerman hello_world2;
hello_world2.Run(); // Prints "Hallo Welt!".
}这一切都非常好和优雅,但是管理/存储此类可配置对象集合的惯用方式是什么?例如,有人想要这样写
std::vector< some_magic_type > seasons_greetings; // What is the common type specifying the public interface only?
seasons_greetings.push_back(hello_world); // which is of type HelloWorldEnglish
seasons_greetings.push_back(hello_world2); // which is of type HelloWorldGerman
for (greeting : seasons_greetings) {
greeting.Run() // access only the public interface
}在将接口设计为基类并从中派生专门的实现时,我没有这个问题-我总是可以存储指向基类类型的指针-但我需要详细说明导致大量派生类的所有实现。基于策略的设计承诺通过使用模板混合和匹配行为来缓解随之而来的派生类的爆炸性增长。但我为此付出了很多不同的类型。
必须有一种惯用的方法来处理这个问题。任何洞察力都是非常值得欣赏的。
另外,我承认我没有买这本书,但你可能已经猜到了。这个answer建议存储一个集合意味着基于继承的设计,但它真的是这样吗?
发布于 2020-12-22 07:26:53
所以这是我在重新思考评论后找到的解决方案- key似乎结合了两个世界的最好的东西,多态性和政策-谁会想到...
通过引入一个仅定义behavior类的公共接口的公共基类,然后从该基类和策略派生behavior类,我可以准确地获得我想要的:
class HelloWorldInterface {
public:
// Behavior method.
void Run() const {
run();
}
private:
virtual void run() const = 0;
};
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : public HelloWorldInterface, private OutputPolicy, private LanguagePolicy {
private:
using OutputPolicy::Print;
using LanguagePolicy::Message;
void run() const override final {
Print(Message());
}
};然后,我基本上可以从一开始就编写我想要的内容:
int main() {
// Example 1
using HelloWorldEnglish = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>;
// Example 2
// Does the same, but uses another language policy.
using HelloWorldGerman = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>;
HelloWorldEnglish hello_world;
HelloWorldGerman hello_world2;
std::vector<const HelloWorldInterface*> greetings{&hello_world, &hello_world2};
for (auto x : greetings) {
x -> Run();
}
}这样,就可以获得一个公共接口,该接口公开由任意策略组合产生的行为。不过,现在看起来微不足道。
发布于 2020-12-21 07:17:39
如果你想继续使用不相关的类,你可以结合使用std::visit和std:variant。
示例(只需扩展原始示例中的main函数)
using Variant = std::variant< HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>, HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman> >;
std::vector< Variant > seasons_greetings;
seasons_greetings.push_back(hello_world);
seasons_greetings.push_back(hello_world2);
for (auto& greeting : seasons_greetings) {
std::visit( [](auto& what){ what.Run(); }, greeting );
} 不好的一面:你必须知道所有可能的政策组合,这可能真的很不舒服。但是你可以使用一些元模板的东西来创建所有的变体类型,通过为每个使用的策略提供类型列表,模板将生成所有的组合,这并不是什么大麻烦。
https://stackoverflow.com/questions/65385679
复制相似问题