Factory Pattern Updated

October 13, 2008 by Tor Brede Vekterli

After posting the Boost-Centric Factory Pattern Implementation article, I got some excellent feedback and advice on things that could be improved. Turns out that was most of the (non-abstract) factory code! I'm leaving the old implementation on archive in case anyone prefers its way of doing things. This post outlines the bulk of the changes made and the reasoning behind them.

The most drastic change comes in how the base type and the creator-functions are specified and handled. On the previous implementation one would specify the base type and argument list explicitly using two template parameters, one involving an MPL sequence. As I was made aware of, this of course isn't a very natural way of specifying something that would be much more suited as a single function signature, since we already require all creator functions to match a certain signature (must return an instance that is derived from some base type, taking a given set of argument types).

Ye olde approache:

typedef factory<
    foo
  , std::string
  , mpl::vector<int>
> myfactory_t;

New, shiny and generic approach:

typedef factory<
  foo*(int)
> myfactory_t;

Note that foo is now given as foo*, since the old code implicitly wrapped all its return values in a std::auto_ptr, which is bad mojo for truly generic code. Now the return type given in the function signature is the one that is actually returned. To get the same end-result as the old factory, one would then just use factory<std::auto_ptr<foo>(int)>. Note that this also lets us get away with not specifying the identifier-type every time if we're happy with the default value (std::string).

Speaking of default values, another major change is the use of Boost.Parameter for the template parameters. Now all parameters except the function signature (which always has to be present as the very first parameter, as it seems Boost.Parameter does not cope very well with function style template parameters) can be given in an arbitrary order, as long as they are properly "tagged". Since the error policy is the last parameter of the template, changing it would normally imply that we had to give template values for all the parameters coming before it. No more, no siree! This will suffice:

typedef factory<
    foo*(int)
  , error_policy<factory_exception_policy>
> myfactory_t;

Here, as one might imagine, error_policy<> is the tag used to identify the parameter. It's up to the user to decide what approach to use, and the standard

typedef factory<
    foo*(int)
  , std::string
  , std_map_adapter
  , factory_exception_policy
> myfactory1_t;

is functionally equivalent to

typedef factory<
    foo*(int)
  , id_type<std::string>
  , map_type<std_map_adapter>
  , error_policy<factory_exception_policy>
> myfactory2_t;

or even

typedef factory<
    foo*(int)
  , map_type<std_map_adapter>
  , error_policy<factory_exception_policy>
  , id_type<std::string>
> myfactory2_t; 

which also shows all the available tags and again stresses the fact that tagged parameters are order-agnostic.

The way creator functions are called has also been changed. Whereas before it would be done indirectly through a create-method, it is now done using operator[]:

foo* obj = my_factory["my-fancy-id"](1234); 

This is consistent with how maps are normally interacted with, and has the added benefit of removing a layer of indirection and argument forwarding, as operator[] returns a const reference to the internal function object itself.

This has been a general overview of the changes made to the standard factory code, and the motivations behind them. To see a more in-depth explanation of how to use it, see the (now updated) main factory-pattern post.

Comments

BHW on April 14, 2017

each time i used to read smaller posts that as well
clear their motive, and that is also happening
with this piece of writing which I am reading now.

Post a comment