Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem using abstract factory

I am using an abstract factory to create user interface components such as dialogs. The abstract factory used is returned from a currently selected generic "INode" which is the base class for several different types of node. So for instance, if I want to add a new node of the same type as the selected node, the scenario goes something like this:

(please note this is semi-pseudo code)

User clicks node and the node gets stored for later use:

void onTreeNodeSelected(INode *node)
{
    selectedNode = node;
}

User clicks "add" on the user interface:

void onAddClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createAddDialog(parentWidget);
    dialog->show();
}

Which all seems fine. The problem comes when I want to edit the selected node:

void onEditClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createEditDialog(selectedNode, parentWidget);
    dialog->show();
}

Oh dear.. I'm passing in an INode object. At some point I'm going to have to downcast that to the correct node type so the dialog can use it properly.

I've studied the "PostgreSQL Admin 3" source code, and they do something similar to this. They get round it by doing something like this:

FooObjectFactoryClass::createDialog(IObject *object)
{
    FooObjectDialog *dialog = new FooObjectDialog((FooObject*)object);
}

Yeck.. cast!

The only way I can think around it and still able to use my factories is to inject the node itself into the factory before it is returned:

FooNode : INode
{
    FooNodeFactory* FooNode::getFactory()
    {
        fooNodeFactory->setFooNode(this);
        return fooNodeFactory;
    }
}

So then my edit event can do this:

void onEditClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createEditDialog(parentWidget);
    dialog->show();
}

And it will use the injected node for context.

I suppose if there is no injected code, the createEditDialog could assert false or something.

Any thoughts?

Thanks!

like image 778
Mark Avatar asked Aug 02 '10 17:08

Mark


People also ask

What are the disadvantages of Abstract Factory design pattern?

Use of this pattern makes it possible to interchange the concrete classes without changing the client code even at runtime. Drawback: One of the main drawbacks is the extra complexity and writing the code during the initial stages.

What problem does Abstract Factory solve?

The Abstract Factory design pattern solves problems like: How can an application be independent of how its objects are created? How can a class be independent of how the objects it requires are created? How can families of related or dependent objects be created?

What are the consequences of applying the Abstract Factory pattern?

Abstract-Factory Pattern { consequence: The Abstract Factory pattern has the following beneets and liabilities: 1. It isolates concrete classes. 2. It makes exchanging product families easy.

What can be the disadvantages of Factory Method?

A potential disadvantage of Factory methods is that clients might have to sub-class the creator class just to create a particular concrete product object. Subclassing is fine when the client has to subclass the creator class anyway, but otherwise, the client now must deal with another point of evolution.


2 Answers

In my opinion, using C-style casts (although C++ style would be preferred) is perfectly acceptable as long as your code is properly commented.

I'm not a big fan of DI (dependency injection) because it makes some code hard to follow and, in your case, I would rather look at a dynamic_cast<>() or something than try to follow injected code over multiple source files.

like image 43
David Titarenco Avatar answered Sep 28 '22 22:09

David Titarenco


A common solution is "double-dispatch", where you call a virtual function on one object, which in turn calls a virtual function on the other, passing this, which now has the correct static type. So, in your case, the factory can contain "create" functions for the various types of dialogues:

class IFactory
{
public:
    ....
    virtual Dialog* createEditDialog(ThisNode*, IWidget*);
    virtual Dialog* createEditDialog(ThatNode*, IWidget*);
    virtual Dialog* createEditDialog(TheOtherNode*, IWidget*);
    ....
};

then each type of node has a virtual createEditDialog that dispatches to the correct factory function:

class INode
{
public:
    ....
    virtual Dialog* createEditDialog(IWidget* parent) = 0;
    ....
};

class ThisNode : public INode
{
public:
    ....
    virtual Dialog* ThisNode::createEditDialog(IWidget* parent)
    {
        return getFactory()->createEditDialog(this, parent);
    }
    ....
};

Then you can create the correct dialogue as

void onEditClicked()
{
    Dialog *dialog = selectedNode->createEditDialog(parentWidget);
    dialog->show();
}
like image 120
Mike Seymour Avatar answered Sep 28 '22 21:09

Mike Seymour