3

I'm trying to create an abstract factory pattern where I have two factories, lets call them factoryA and factoryB. I'll use struct instead of class to save some typing.

For some family of inherited classes, lets start with some class A,

struct A
{
  // ...
};

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

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

Now let, for illustration purposes do the same thing with some other class B.

struct B
{
  // ...
};

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

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

So for these classes I'm trying to create a factory pattern as so (Note at my job we use custom error handling I'll call error_handle_t whose implementation shouldn't matter):

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 ...
    }
};

It would seem like an "interface" abstract class could be an appropriate base class but the return types differ for each factory. And class A and B don't have a common base class between them to make this work "nicely" like that so I can't do

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

but the above factories seem pretty boilerplate and could almost be parameterized as so

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 ...

    }
};

According to Override template member in Interface, I can't make the template class virtual for this to be an abstract class.

But if we can assume the "algorithm" of make_object is the same. I read online that this could be a utility class such that

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 ...
    }
};

But I'm not overriding the template base and just using it as a helper function. Doesn't really solve what I want to achieve.

The final goal was to have an abstract factory such that

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

Instead of create the same object without a common base class.

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 ...;
    }
};

So I have to make a create method as many factories as it contains without leverage templates and type deduction in some clean way. Thoughts or recommendations?

the usage I wanted:

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);

versus what I have:

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);
Andre Marin
  • 540
  • 1
  • 6
  • 16
  • Can you provide an example of how you would like to use `factories`? – Chris Drew May 17 '18 at 19:46
  • yes, please show how you would use a `factories` object to `create` a `A` and a `B` – Ryan Haining May 17 '18 at 19:52
  • *"I can't make the template class virtual"*. You cannot have template virtual method, but you can have virtual method in template class (`base::make_object` can be `virtual`). – Jarod42 May 17 '18 at 20:19
  • `A` and `B` seems unrelated, so having `CreateA` and `CreateB` seems the correct interface. – Jarod42 May 17 '18 at 20:21
  • It seems like you may just want `template std::shared_ptr make(...);`. Seems a lot like `std::make_shared`. – François Andrieux May 17 '18 at 20:29
  • 1
    *"I'm trying to create an abstract factory pattern where I have two factories, lets call them `factoryA` and `factoryB`"*, For abstract factory, would make more sense to have `factoryA1` and `factoryA2`. – Jarod42 May 17 '18 at 20:32
  • @Jarod42 They are just example names. Both names just mean they are distinct from each other. Just a suggestion for clarity? A and B are related in that they submodules to a bigger module. But they are managed more easily separately. The factory pattern was meant to get back the right object. They are related in that they share the same data to construct. because I put them in a facade https://stackoverflow.com/questions/50312460/c-class-design-base-class-inheritance-or-facade-design-pattern – Andre Marin May 17 '18 at 22:13
  • @ChrisDrew done. – Andre Marin May 17 '18 at 22:13
  • 1
    I meant here with `FactoryA`/`factoryB` you need to compose factories, whereas abstract repository is to substitute factory, so `FactoryA1`/`FactoryA2` (which both creates `A`). – Jarod42 May 17 '18 at 22:17
  • @Jarod42 so the use of an abstract factory is probably incorrect for this case? – Andre Marin May 17 '18 at 22:20
  • 1
    That term seems to be used incorrectly, indeed. – Jarod42 May 17 '18 at 22:23

2 Answers2

1

You probably want something like:

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>;

In C++11, factories would be:

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;
};

and then

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);
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

To be fair I'm not sure what are you loking for... This should probably be a comment rather then a proposed answer but this is the only way I know to write properly formatted code. Please sorry me.

This sentence make me think that you are looking for something like:

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()
    {
      // ...
    }
};

does it sounds? I Cannot feature out what's the role of derived classes.