Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The "Self-Factory" Pattern

Tags:

c++

factory

I don't know if there is an official name for this, but I have been playing with what I like to call the "self-factory" pattern. Basically, it's when an abstract base class acts as a factory for itself. Let me explain:

I have Foo objects and Bar objects in my system, which are used via interfaces FooInterface and BarInterface. I need to give my clients the right type of Foo and Bar. The decision of which concrete Foo object to create is made at compile time. For example, if you compile on win32, you want to only create Win32Foo objects, and if you compile on OSX you want to only create OSXFoo objects and so on. But, the decision of which concrete Bar object to create is made at runtime, based on a key string.

Now, my question is about the best way to implement this scheme. One way I come up with uses regular factories:

shared_ptr<FooInterface> foo = FooFactory::create();
shared_ptr<BarInterface> happyBar = BarFactory::create("Happy");
shared_ptr<BarInterface> sadBar = BarFactory::create("Sad");

Another way is to use what I call "self-factories":

shared_ptr<FooInterface> foo = FooInterface::create();
shared_ptr<BarInterface> happyBar = BarInterface::create("Happy");
shared_ptr<BarInterface> sadBar = BarInterface::create("Sad");

What are the pros and cons of each approach, both from a usability standpoint and from an architectural standpoint?

like image 420
cwick Avatar asked May 23 '26 23:05

cwick


2 Answers

I'd make an improvement:

shared_ptr<FooInterface> foo = Factory<FooInterface>::create();
shared_ptr<BarInterface> happyBar = Factory<BarInterface>::create("Happy");
shared_ptr<BarInterface> sadBar = Factory<BarInterface>::create("Sad");

You'd declare:

template <class I>
struct Factory { };

And then for each interface that needs a factory, you'd do this:

template <>
struct Factory<FooInterface>
{
    static FooInterface create();
};

This allows you to keep the factory implementation separate from the interface declaration, but still using the type system to bind them at compile time.

like image 190
Daniel Earwicker Avatar answered May 25 '26 14:05

Daniel Earwicker


Factories have two common uses:

1) Decide dynamic polymorphic type at runtime, based on parameters and/or global state (such as configuration). Your pattern does this.

2) Dependency injection: rather than using a static function to create objects, use a factory object, so that the type of object returned can be configured by the caller, by passing in whatever factory they want. Neither of these patterns provides this. Furthermore, your second pattern doesn't even allow static dependency injection (by having template functions that take a factory class as a template parameter), because the interface and the factory are the same.

So one con of your pattern (and of your regular factories) is that dependency injection isn't really supported. There is one and only one function to call to get an object that's a FooInterface, and that is FooInterface::create(). I'll not argue why dependency injection is useful, just point out that if you build this way, you can't use it.

like image 39
Steve Jessop Avatar answered May 25 '26 13:05

Steve Jessop



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!