使用抽象工厂的问题

Mar*_*ark 6 c++ oop design-patterns

我正在使用抽象工厂来创建用户界面组件,如对话框.使用的抽象工厂从当前选择的通用"INode"返回,它是几种不同类型节点的基类.因此,例如,如果我想添加与所选节点相同类型的新节点,则场景如下所示:

(请注意这是半伪代码)

用户单击节点并存储节点以供以后使用:

void onTreeNodeSelected(INode *node)
{
    selectedNode = node;
}
Run Code Online (Sandbox Code Playgroud)

用户点击用户界面上的"添加":

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

一切似乎都很好.当我想编辑所选节点时出现问题:

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

哦亲爱的..我正在传递一个INode对象.在某些时候,我将不得不将其转发到正确的节点类型,以便对话框可以正确使用它.

我研究了"PostgreSQL Admin 3"源代码,他们做了类似的事情.他们做这样的事情绕过它:

FooObjectFactoryClass::createDialog(IObject *object)
{
    FooObjectDialog *dialog = new FooObjectDialog((FooObject*)object);
}
Run Code Online (Sandbox Code Playgroud)

哎呀..演员!

我可以考虑它并仍能使用我的工厂的唯一方法是在返回之前将节点本身注入工厂:

FooNode : INode
{
    FooNodeFactory* FooNode::getFactory()
    {
        fooNodeFactory->setFooNode(this);
        return fooNodeFactory;
    }
}
Run Code Online (Sandbox Code Playgroud)

那么我的编辑事件可以这样做:

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

它将使用注入的节点作为上下文.

我想如果没有注入代码,createEditDialog可以断言false或者什么.

有什么想法吗?

谢谢!

Mik*_*our 3

一种常见的解决方案是“双重调度”,其中您在一个对象上调用虚拟函数,该对象又调用另一个对象上的虚拟函数,并传递this,该对象现在具有正确的静态类型。因此,在您的情况下,工厂可以包含各种类型对话的“创建”功能:

class IFactory
{
public:
    ....
    virtual Dialog* createEditDialog(ThisNode*, IWidget*);
    virtual Dialog* createEditDialog(ThatNode*, IWidget*);
    virtual Dialog* createEditDialog(TheOtherNode*, IWidget*);
    ....
};
Run Code Online (Sandbox Code Playgroud)

那么每种类型的节点都有一个虚拟的createEditDialog,可以分派到正确的工厂函数:

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);
    }
    ....
};
Run Code Online (Sandbox Code Playgroud)

然后你可以创建正确的对话

void onEditClicked()
{
    Dialog *dialog = selectedNode->createEditDialog(parentWidget);
    dialog->show();
}
Run Code Online (Sandbox Code Playgroud)