• Advertisement
Sign in to follow this  

Policy-based design question

This topic is 4039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So, it seems like I've got an ideal candidate for a bit of policy-based design on my hands here, and I'd quite like to try it out. Basically, it's for a 'Factory' (perhaps poorly named), which can either create new objects and return smart pointers to them, or can create only the first instance and hand back smart pointers to this thereafter. I forsee some other creation behaviours coming along after this, which is why I think there should be some kind of 'CreationPolicy'. Now, I think I'm just about able to actually implement this, but I'm confused about how it would be used. In my project, there is a sort of 'registry' of Factory objects (just an STL map, behind the scenes), from which an appropriate factory can be requested. It's done this way because I really don't want to use singletons, yet lots of things will need access to the Factories. As such, all Factory classes inherit from FactoryBase. The thing is, I can see code looking something like this, which I don't think is right at all:
Factory<ThingType, CreationPolicy> f = dynamic_cast<Factory<ThingType, CreationPolicy>(getFactory("factory name"));
ThingType* thing = f.create("thing name");
Specifically, I don't like the cast, and I don't like having to know what policy was used - what if the code that registers the factory in the first place decides to change the creation policy? I don't know how to make this better, so that's where you fellas come in :) Can anyone recommend a good way to do this? Cheers :)

Share this post


Link to post
Share on other sites
Advertisement
There are two parts to your system. The first is the fact that the policy is an implementation detail. This detail should be abstracted away as either a derived class or a constructor argument


// Version 1:
// Class manipulated by factory users
template<typename Object>
class factory : public FactoryBase
{
public: virtual ptr<Object> create() = 0;
};

// Class manipulated by factory creators
template<typename Object,typename Policy>
class factory_impl : public factory<Object>
{
public: ptr<Object> create();
};

// Version 2:
// Non-abstract class manipulated by everyone
template<typename Object>
class factory : public FactoryBase
{
public:
factory(Policy& policy);
ptr<Object> create();
};



The second problem is that you use a registry of factory objects and lose all type information by placing factories in this registry, but still insist on retrieving type information when obtaining a factory from the registry. There are two options here: redesign your system so retrieving type information is not necessary (the STL map contains FactoryBase, the user uses FactoryBase), or redesign your system so type information is never lost.


// Method 1:
FactoryBase & f = getFactory("factory name");
ptr<Object> o = f.create("thing name");

// Method 2:
factory<ThingType> & f = getFactory<ThingType>("factory name");
ptr<ThingType> thing = f.create("thing name");

Share this post


Link to post
Share on other sites
You can move the policy from the template parameter, to the constructor:

//not this:
template<typename ThingType, typename Policy>
class Factory {
Factory();
};

//but this:
template<typename ThingType>
class Factory {
Policy policy;
Factory(Policy policy);
};



Your code would look like this:

RegisterFactory("MyFac", new MyFactory<ThingType>(new SmartPolicy));
...
Factory<ThingType> f = dynamic_cast< Factory<ThingType> >(getFactory("factory name"));

ThingType* thing = f.create("thing name");



Or you could do something like:

//(***)
template<typename ThingType>
Factory<ThingType>* getFactory(const char* name)
{
return dynamic_cast< Factory<ThingType>* >(getFactoryRaw(name));
}

//and use it like this:
Factory<ThingType>* f = getFactory<ThingType>("factory name");

ThingType* thing = f->create("thing name");



Or, if you need the policy to be a template parameter, then just do this:

template<typename ThingType>
class Factory {
};

template<typename ThingType, typename Policy>
class RealFactory : Factory<ThingType> {
RealFactory();
};

RegisterFactory("MyFac", new MyRealFactory<ThingType,SmartPolicy>());
...
//and use the (***) function anyway:
Factory<ThingType>* f = getFactory<ThingType>("factory name"));

ThingType* thing = f->create("thing name");


Share this post


Link to post
Share on other sites
Thanks guys, that's a huge help, actually.

One question though: ToohrVyk, when you said this:

Original post by ToohrVyk
factory<ThingType> & f = getFactory<ThingType>("factory name");
ptr<ThingType> thing = f.create("thing name");
[/quote]

Did you mean for getFactory to implemented as Zbyl said? i.e.

template<typename ThingType>
Factory<ThingType>* getFactory(const char* name)
{
return dynamic_cast< Factory<ThingType>* >(getFactoryRaw(name));
}


If so, then I'll get on and implement it! If not, how else would you go about doing this?

Share this post


Link to post
Share on other sites
Quote:
Original post by hymerman
Did you mean for getFactory to implemented as Zbyl said? i.e.


No, there's no sense in losing type information only to retrieve it later on. I would store objects in separate maps (which would also increase search performance, since the maps are smaller).


template<typename Object>
class factory_handler
{
static std::map< std::string,factory<Object>* > data;
public:
void set(const std::string& name, factory<Object>* f)
{
data[name] = f;
}
factory<Object>* get(const std::string& name)
{
return data[name];
}
};

template<typename Object>
factory<Object>* get(const std::string& name)
{
return factory_handler<Object>::get(name);
}

template<typename Object>
void set(const std::string& name, factory<Object>* f)
{
factory_handler<Object>::set(name,f);
}




EDIT: register is a keyword... Well, that's not nearly as silly as writing a type inference algorithm in a language where type is a keyword [rolleyes]

[Edited by - ToohrVyk on January 28, 2007 1:16:32 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement