首页 > 解决方案 > 基于策略的设计 - 处理多种类型的理想方式,例如将它们存储在容器中、迭代等

问题描述

在来自维基百科的基于策略的设计的 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
}

在将接口设计为基类并从它们派生专门的实现时,我没有这个问题——我总是可以存储指向基类类型的指针——但我需要说明导致大量派生类的所有实现。基于策略的设计承诺通过使用模板来混合和匹配行为来缓解派生类的爆炸式增长。但我为此付出了很多不同的类型。

必须有一种惯用的方法来处理这个问题。非常感谢任何见解。

PS我承认我没有买这本书,但你可能已经猜到了。这个答案表明存储集合意味着基于继承的设计,但真的如此吗?

标签: c++templatesdesign-patternsc++14c++17

解决方案


如果您想保留不相关的类,可以将std:variantstd::visit结合使用。

示例(只需从原始示例扩展 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 );
    }   

坏的一面:你必须知道所有可能的政策组合,这些组合可能真的很不舒服。但是您可以使用一些元模板的东西通过为每个使用的策略提供类型列表来创建所有变体类型,并且模板将生成所有组合,这并不是一个大黑客。


推荐阅读