Sign in to follow this  
SAE Superman

Object Factory question

Recommended Posts

So I understand object factories and how to use them when the class hierarchy is one layer deep. But how can you use a factory for objects that are 2+ levels deep? Below is an example:
Class Animal
{
    void attack() = 0;
};

Class Cat : public Animal
{
    void attack() = 0;
    void meow() = 0;
};

Class Dog : public Animal
{
    void attack() = 0;
    void bark() = 0;
};

Class ConcreteCat
{
    //implementation of abstract Cat
};

Class ConcreteDog
{
    //implementation of abstract Dog
};


So now I want an Animal factory...do I need to have separate factories, one for cats and one for dogs? Is it good/bad to have an Animal factory and then have to cast the Animal* to a Cat* or Dog*? Thanks in advance!

Share this post


Link to post
Share on other sites
You would need to identify each concrete type separately, and then have your factory switch on the specific one you want to create. The depth of the hierarchy shouldn't matter, unless you want to have a factory function at each level that calls yet another factory function, etc. But something like this works just fine:

Animal* IWantMyselfAnAnimal(int id)
{
switch(id)
{
case DOG:
return new ConcreteDog();
case CAT:
return new ConcreteCat();
default:
message("While probably tasty, not what I was expecting!");
return NULL;
}
}

Share this post


Link to post
Share on other sites
So in your example, your factory returns an Animal*...so what if I have a piece of code that needs to create a ConcreteCat and make the cat meow? Do I have to cast like this:


Cat* pCat = dynamic_cast<Cat*>( IWantMyselfAnAnimal(CAT) );
pCat->meow();

Share this post


Link to post
Share on other sites
There are different ways to build factories and it partially depends on what it is you actually want. Your example somewhat lacks a real problem which makes suggesting the right solution tricky.

Zipster's factory is an Animal factory. There'd be little reason to cast the Animal* to anything else; if you knew you wanted a dog or cat you'd just do:

ConcreteDog dog; //or
ConcreteCat cat;

Share this post


Link to post
Share on other sites
Specifically, this is for a plugin system...so I have an abstract Plugin interface, then abstract saving/loading plugins, and abstract renderer plugin, etc. I then have concrete saving/loading plugins, and concrete renderer plugins...

Share this post


Link to post
Share on other sites
In that case a PluginFactory is probably not going to do you much good when you consider how different the configuration of a renderering module is from a serialisation module.

If working with that design I would have factory functions like createRenderer and so on - so that is to have factories for the more specialised classes rather than the most generalised class.

Share this post


Link to post
Share on other sites
Quote:
Original post by SAE Superman
So is it true that in general, factories have to be for one 'level' up on the class hierarchy (or actually, for whatever interface you need to work with)?
I'm not sure I'd say it's a rule of thumb to be honest. You certainly could have a plugin-factory if you actually were just interested in interacting through just the plugin interface. Also factories can have an inheritance hierarchy all of their own; you can subclass your factories from other factories which in some ways would botch a generalisation like they must be one 'level' up.

Concerns about where to 'place' a factory along the inheritance chain of it's products are uncommon these days fortunately. In modern C++ programming inheritance hierarchies have become shallow anyway, seldom needing more than, say, one level of inheritance. Things are much more obvious and easier to manage that way. In regards to this, what does your Plugin base class actually do?

Share this post


Link to post
Share on other sites
It depends really, composition (has-a something) is usually cited as being favourable to inheritance (is-a something). I can certainly make an easy example use of it for plugins - though I'm obviously not familiar with your actual system to know if it would work for you.

Ex:


// Just the interface because I'm lazy
class PluginDescription
{
PluginDescription(const std::string & n, const std::string & v);
const std::string & name();
const std::string & version();
};

// Then each plugin module will be composed using that
class Renderer
{
public:
const PluginDescription & plugin() { return pd; }

protected:
Renderer(const PluginDescription & pd) : pd(pd) { }

private:
const PluginDescription pd;
};


As you can see composition usually requires more 'leg work' to get it going but when you put together larger system it leads more elegant designs.

In this case I can see reasons for and against inheritance (is-a plugin) versus composition (has-a plugin name/version). I can't see there being much harm to either option here either. If you need to treat all plugins the same for some reason then inheritance might be the way to go, like storing a collection of all plugins regardless of what they are - cannot think of a reason to do that here though what with your plugins being essentially whole subsystems (surely you'll always have a renderer and it's just a matter of choosing which renderer?)

Share this post


Link to post
Share on other sites
You have two basic approaches. First is having one and only master plug-in definition:

struct PlugIn {
virtual void load() = 0;
virtual void unload() = 0;

virtual error_t foo() {
return E_NOT_SUPPORTED;
}

virtual error_t bar(const char *name) {
return E_NOT_SUPPORTED;
}

virtual error_t baz(X * x) {
return E_NOT_SUPPORTED;
}

bool has_capability(Capability c) {
return false;
}
};

All plugins extend the above. Every function defines some functionality, and functionality as a whole. Plugin author extends the interface and implements what's necessary.

Due to default implementation, they only override what they actually support, and adjust has_capability accordingly.

Another approach is interface per capability:
struct Plugin {
virtual void load() = 0;
virtual void unload() = 0;
};

struct Bar : public PlugIn {
virtual do_bar() = 0;
};



Authors extend the plugin the want, and implement the virtual method(s).

Another option would be using DLLs.


Next, comes the instantiation of the implementations. A plugin is a basically a tuple of (functionality, implementation). What you need to decide now is the actual relation. For each functionality, can there be (0 or 1), exactly 1 or more than 1 plug-in.

Once you decide on that, you can write your factory. For first two cases, the abstract factory will work.

Read implementation descriptors from some file, or search a directory to populate the factory. Then you get the basic interface:
// first case, with uniform interface
PlugIn * createInstance(const std::string &functionality);
...
PlugIn * bar_plugin = createInstance("bar");
PlugIn * foo_plugin = createInstance("foo");


// second case, typical templated solution
template < class Functionality >
class PlugInFactory {
PlugIn * createInstance();
};
...
PlugInFactory<Foo> fooFactory("/plugins/foo.cfg");
PlugInFactory<Bar> barFactory("/plugins/bar.cfg");
PlugInFactory<Baz> bazFactory("/plugins/baz.cfg");
...
Bar * barPlugin = barFactory.createInstance();[/code]

Additional information can be tucked into various parts of plugin implementation (names, parameters, etc..).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this