关于抽象工厂和注射的问题

时间:2010-08-03 13:37:25

标签: c++ design-patterns oop

这与我的其他一个问题类似,但我认为不同的是保证一个新问题。

基本上我正在编写用户界面,我的用户界面有可以选择的节点。当选择节点时,用户界面以抽象节点基类“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,基类每次都可以返回注入抽象工厂类..

3 个答案:

答案 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();
    ...
}

也许,它适合您的所有需求)