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

(请注意,这是半伪代码)

用户单击节点,节点将被存储以供以后使用:

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

用户在用户界面上单击“添加”:
void onAddClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createAddDialog(parentWidget);
    dialog->show();
}

一切似乎都很好。当我要编辑所选节点时出现问题:
void onEditClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createEditDialog(selectedNode, parentWidget);
    dialog->show();
}

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

我研究了“PostgreSQL Admin 3”源代码,并且他们做了类似的事情。他们通过执行以下操作来绕过它:
FooObjectFactoryClass::createDialog(IObject *object)
{
    FooObjectDialog *dialog = new FooObjectDialog((FooObject*)object);
}

哎呀.. Actor !

我可以考虑并仍然能够使用我的工厂的唯一方法是在返回节点之前将节点本身注入(inject)工厂:
FooNode : INode
{
    FooNodeFactory* FooNode::getFactory()
    {
        fooNodeFactory->setFooNode(this);
        return fooNodeFactory;
    }
}

因此,我的编辑事件可以做到这一点:
void onEditClicked()
{
    IFactory *factory = selectedNode->getFactory();
    Dialog *dialog = factory->createEditDialog(parentWidget);
    dialog->show();
}

并且它将注入(inject)的节点用于上下文。

我想如果没有注入(inject)的代码,则createEditDialog可以断言false或类似的东西。

有什么想法吗?

谢谢!

最佳答案

常见的解决方案是“双分派(dispatch)”,您可以在一个对象上调用虚拟函数,然后在另一个对象上调用虚拟函数,并传递this,现在它具有正确的静态类型。因此,在您的情况下,工厂可以包含用于各种类型对话的“创建”功能:

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

那么每种类型的节点都有一个虚拟的createEditDialog,它会分派(dispatch)给正确的工厂函数:
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);
    }
    ....
};

然后,您可以按照以下方式创建正确的对话
void onEditClicked()
{
    Dialog *dialog = selectedNode->createEditDialog(parentWidget);
    dialog->show();
}

10-02 02:33
查看更多