【问题标题】:Clean way to use a template base class to eliminate redundancy on derived classes for a factory class使用模板基类消除工厂类派生类冗余的简洁方法
【发布时间】:2018-10-28 04:32:09
【问题描述】:

我正在尝试创建一个抽象工厂模式,其中我有两个工厂,我们称它们为 factoryA 和 factoryB。我将使用 struct 而不是 class 来节省一些输入。

对于一些继承类家族,让我们从一些 A 类开始,

struct A
{
  // ...
};

struct derivedA1 : public A
{
  // ...
};

struct derivedA2 : public derivedA1
{
  // ...
};

现在,为了说明的目的,让其他 B 类做同样的事情。

struct B
{
  // ...
};

struct derivedB1 : public B
{
  // ...
};

struct derivedB2 : public derivedB1
{
  // ...
};

因此,对于这些类,我正在尝试创建一个工厂模式(请注意,在我的工作中,我们使用自定义错误处理,我将调用 error_handle_t,其实现无关紧要):

struct factoryA
{
    std::map<int, std::shared_ptr<A> > myMap;

    factoryA()
    {
        // setup values to search against
        myMap[0] = std::make_shared<A>();
        myMap[1] = std::make_shared<derivedA1>();
        myMap[2] = std::make_shared<derivedA2>();

        //...room for more
    }

    error_handle_t make_object(int key, std::shared_ptr<A>& object)
    {
        object = myMap[key];
        return ...
    }
};

struct factoryB
{
    std::map<int, std::shared_ptr<B> > myMap;

    factoryB()
    {
        // setup values to search against
        myMap[0] = std::make_shared<B>();
        myMap[1] = std::make_shared<derivedB1>();
        myMap[2] = std::make_shared<derivedB2>();
        //...room for more
    }

    error_handle_t make_object(int key, std::shared_ptr<B>& object)
    {
        object = myMap[key];
        return ...
    }
};

看起来“接口”抽象类可能是适当的基类,但每个工厂的返回类型不同。而且 A 类和 B 类之间没有共同的基类来使这项工作像那样“很好”,所以我不能这样做

error_handle_t make_object(int key, std::shared_ptr<base_for_A_and_B>& object) = 0;

但上述工厂看起来很样板,几乎可以这样参数化

template <typename T>
struct base
{
    std::map<int, std::shared_ptr<T> > myMap;

    error_handle_t make_object(int key, std::shared_ptr<T>& object)
    {
        object = myMap[key];
        return ...

    }
};

根据Override template member in Interface,我不能将模板类虚拟化为抽象类。

但是如果我们可以假设 make_object 的“算法”是相同的。我在网上读到这可能是一个实用程序类,这样

struct factoryA : private base<A>
{
    std::map<int, std::shared_ptr<A> > myMap;

    error_handle_t make_object(int key, std::shared_ptr<A>& object)
    {
        ...
        object = myMap[key];
        return ...
    }
};

但我并没有覆盖模板库,只是将其用作辅助函数。并不能真正解决我想要实现的目标。

最终目标是有一个抽象工厂,这样

struct factories
{
    template <typename T>
    error_handle_t create(int key, shared_ptr<T>& object)
    {
        object = make_shared<T>()->make_object(key)
        return ...;
    }
};

而不是创建没有公共基类的相同对象。

struct factories
{
    // Initialize in ctor which I didn't include here
    factoryA concrete_factoryA;
    factoryB concrete_factoryB;

    error_handle_t createA(int key, shared_ptr<A>& object)
    {
       object = concrete_factoryA.make_object(key);
       return ...;
    }

    error_handle_t createB(int key, shared_ptr<B>& object)
    {
       object = concrete_factoryB.make_object(key);
       return ...;
    }
};

因此,我必须创建一个包含与其包含的工厂一样多的创建方法,而无需以某种干净的方式利用模板和类型推导。想法或建议?

我想要的用法:

factories l_factories;
shared_ptr<A> a_ptr;
shared_ptr<B> b_ptr;

l_factories.create(0, a_ptr);
l_factories.create(1, b_ptr);

与我所拥有的相比:

factories l_factories;
shared_ptr<A> a_ptr;
shared_ptr<B> b_ptr;

l_factories.createA(0, a_ptr);
l_factories.createB(1, b_ptr);

【问题讨论】:

  • 你能提供一个你想如何使用factories的例子吗?
  • 是的,请说明如何使用factories 对象到createAB
  • "我正在尝试创建一个抽象工厂模式,其中我有两个工厂,我们称它们为 factoryAfactoryB",对于抽象工厂,可以拥有factoryA1factoryA2 更有意义。
  • 我在这里的意思是FactoryA/factoryB 你需要组合工厂,而抽象存储库是替代工厂,所以FactoryA1/FactoryA2(两者都创建A)。
  • 确实,这个词似乎用错了。

标签: c++ c++11 templates inheritance factory


【解决方案1】:

你可能想要这样的东西:

template <typename T>
struct factory
{
    error_handle_t create(int key, shared_ptr<T>& object)
    {
        object = make_shared<T>()->make_object(key)
        return ...;
    }
};

template <typename ... Facts>
struct factories : Facts...
{
    using Facts::create...; // C++17, but can be done in C++11 with recursion
};

using factoryA = factory<A>;
using factoryB = factory<B>;
using MyFactory = factories<factoryA, factoryB>;

在 C++11 中,factories 将是:

template <typename ... Facts> // Main declaration
struct factories {}; // Empty case

template <typename Fact>
struct factories<Fact> : Fact{};

template <typename Fact, typename ... Facts>
struct factories<Fact, Facts...> : Fact, factories<Facts...>
{
    using Fact::create;
    using factories<Facts...>::create;
};

然后

MyFactory l_factories;
shared_ptr<A> a_ptr;
shared_ptr<B> b_ptr;

l_factories.create(0, a_ptr);
l_factories.create(1, b_ptr);

【讨论】:

  • 我试图创建相同的 API,而不是单独的函数。除非用法只是一个错字?你能用你提到的递归给出一个 c++11 的例子吗?
  • 确实是错字。两者都命名为 create。
  • 添加了 C++11 示例。
【解决方案2】:

公平地说,我不确定您在寻找什么......这可能应该是一个评论而不是一个建议的答案,但这是我知道编写格式正确的代码的唯一方法。请原谅我。

这句话让我觉得你在寻找类似的东西:

template<typename T>
struct factory<T>;

template<>
struct factory<A>
{
   std::shared_ptr<A> make_object()
   {
     // ...
   }

}; 

template<>
struct factory<B>
{
    std::shared_ptr<B> make_object()
    {
      // ...
    }
};

听起来不错? 我无法说明派生类的作用。

【讨论】:

  • 这不是同一家工厂吗?
  • 我添加了我想要做的事情与我拥有的东西
  • 也许我遗漏了一些东西,为什么它们是一样的? factory&lt;A&gt;factory&lt;B&gt; 是两种不同的类型。为什么你认为它们是一样的?
猜你喜欢
  • 1970-01-01
  • 2016-11-14
  • 2016-08-17
  • 1970-01-01
  • 1970-01-01
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
相关资源
最近更新 更多