首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >基于策略的设计-处理大量类型的理想方法,例如将它们存储在容器中、迭代等

基于策略的设计-处理大量类型的理想方法,例如将它们存储在容器中、迭代等
EN

Stack Overflow用户
提问于 2020-12-21 07:01:30
回答 2查看 67关注 0票数 0

在来自wikipedia的基于策略的设计的hello world示例中,我们使用一个通用接口HelloWorld,并通过模板使用不同的策略对其进行配置-到目前为止还不错:

代码语言:javascript
运行
复制
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!".
}

这一切都非常好和优雅,但是管理/存储此类可配置对象集合的惯用方式是什么?例如,有人想要这样写

代码语言:javascript
运行
复制
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建议存储一个集合意味着基于继承的设计,但它真的是这样吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-22 07:26:53

所以这是我在重新思考评论后找到的解决方案- key似乎结合了两个世界的最好的东西,多态性和政策-谁会想到...

通过引入一个仅定义behavior类的公共接口的公共基类,然后从该基类和策略派生behavior类,我可以准确地获得我想要的:

代码语言:javascript
运行
复制
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());
   }
};

然后,我基本上可以从一开始就编写我想要的内容:

代码语言:javascript
运行
复制
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();
  }
}

这样,就可以获得一个公共接口,该接口公开由任意策略组合产生的行为。不过,现在看起来微不足道。

票数 1
EN

Stack Overflow用户

发布于 2020-12-21 07:17:39

如果你想继续使用不相关的类,你可以结合使用std::visitstd:variant

示例(只需扩展原始示例中的main函数)

代码语言:javascript
运行
复制
    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 );
    }   

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65385679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档