这与我的其他一个问题类似,但我认为不同的是保证一个新问题。
基本上我正在编写用户界面,我的用户界面有可以选择的节点。当选择节点时,用户界面以抽象节点基类“INode”结束。从这里我通过node-> getFactory()得到一个工厂,从中我可以为该节点创建适当的对话框或视图,因为具体节点返回正确的工厂(例如factory-> createAddDialog(), factory-> createView(node)等。)。
我的问题是试图找到工厂首先进入节点的最佳方式。
到目前为止,我已经想过3种方法:
1)创建节点时注入正确的工厂:
AreaNode *node = new AreaNode(new AreaNodeFactory());
所以AreaNode的定义是:
AreaNode : public INode
{
AreaNode(INodeAbstractFactory *injectedFactory)
{
m_injectedFactory = injectedFactory;
}
INodeAbstractFactory* getFactory()
{
return m_injectedFactory;
}
INodeAbstractFactory* m_injectedFactory;
};
2)注入一个更通用的工厂并允许节点从该工厂获得工厂:
AreaNode : public INode
{
AreaNode(IFactory *injectedFactory)
{
m_injectedFactory = injectedFactory;
}
INodeAbstractFactory* getFactory()
{
return m_injectedFactory->getAreaNodeFactory();
}
IFactory* m_injectedFactory;
}
3)只需创建具体工厂(尽管这会删除在同一节点上使用不同工厂的范围,可能用于测试或以后的更改):
AreaNode : public INode
{
INodeAbstractFactory* getFactory()
{
return new AreaNodeFactory();
}
}
目前对这些选项的看法:
选项1:可能有点偶然 - 我必须确保我总是为它提供正确的工厂,或者我可以使用另一家工厂为我注入正确的工厂。
选项2:强制节点了解抽象工厂实现,足以能够调用getAreaNodeFactory,这可能不是一件坏事。它至少有助于确保始终获取正确/相同的工厂(假设更一般的工厂正确实施)。
选项3:这种限制性很小,因为我无法将类交换出来,而且我并不热衷于节点必须知道工厂的具体实现 - 尽管在这种情况下它可能不会是一个太大的问题(着名的遗言!)。
对此有何想法?
感谢。
编辑: 抱歉错过了原帖中的变量声明,已更正。
编辑: 选项2的另一个问题是我必须在每个节点类型中实现“getFactory”。至少对于选项1,基类每次都可以返回注入抽象工厂类..
答案 0 :(得分:2)
怎么样。
template<typename Factory>
class AreaNode : public INode
{
public:
virtual ~AreaNode(){}
AreaNode() : pFactory_(new Factory())
{
}
const shared_ptr<IFactory>& GetFactory()
{
return pFactory_;
}
private:
shared_ptr<IFactory> pFactory_;
};
编辑:
或取决于您的背景。
template<typename Factory>
class Node
{
public:
virtual ~Node(){}
Node() : pFactory_(new Factory())
{
}
const shared_ptr<IFactory>& GetFactory()
{
return pFactory_;
}
private:
shared_ptr<IFactory> pFactory_;
};
class AreaNode : public Node<AreaNodeFactory>
{
// Implementation
};
// OR
typedef Node<AreaNodeFactory> AreaNode;
答案 1 :(得分:2)
怎么样
class FactoryBase { /* interface */ }
template <typename NodeType> class Factory : public FactoryBase {
// Default implementation.
}
// Add appropriate specializations for all relevant nodetypes, if needed.
template <typename NodeType> inline Factory<NodeType> getFactory(NodeType*) {
return Factory<NodeType>( );
}
class AreaNode : public Node {
FactoryBase getFactory() { return getFactory(this); }
};
模板参数推断将确保工厂类型派生自this
。这应该可以防止手动错误。但总的来说,我根本不打算公开揭露一家工厂。只需将以下位添加到基类:
class Node {
public:
std::Auto_ptr<View> createView( ) {
this->getFactory()->createView(this);
} // etc.
private:
virtual FactoryBase* getFactory() = 0;
};
答案 2 :(得分:1)
取决于“节点”对象的用途
如果工厂应注入到一个节点中,并且具体节点不必对工厂的具体类型执行任何操作,那么所有与工厂相关的代码都可以移动到基本节点类中,从而简化一切
class INode //well, it's not "interface" in clear sense
{
public:
void setFactory(Factory* f) { this->f = f; }
Factory* getFactory() const { return f; }
private:
Factory* f;
};
现在,具体节点和混凝土工厂的功能相互正交,可以任意方式自由组合。
如果具体类型的节点绑定到具体类型的工厂,则可能应该避免使用工厂并使用node :: createView方法直接创建视图。取决于工厂是否在其他方面使用。
还要考虑这个(不是非常OOP方式):
typedef boost::function0<Factory*> Node;
std::map<UIItem*,Node> nodes;
nodes[area_item1] = &getAreaFactory;
nodes[area_item2] = &getAreaFactory;
nodes[region_item1] = &getRegionFactory;
...
void OnSelect(UIItem* i)
{
...
View* v = nodes[i]()->createView();
...
}
也许,它适合您的所有需求)