2014-06-14 152 views
1

我的庫使用對象工廠技術來創建具有特定用途的「節點」。默認情況下,這些節點完成他們的工作,但是非常基礎。我希望允許我的庫的用戶能夠創建提供的「Node」類的子類來定義它們自己的功能,同時保持節點的基本功能不變。 下面是一些示例代碼,以證明我的問題:自定義對象工廠

class Node 
{ 
    int SomeValue; 
public: 
    Node(int Value) 
    { 
     SomeValue = Value; 
    } 

    ~Node() 
    { 
     // Cleanup the node 
    } 
}; 

class NodeFactory 
{ 
    std::vector<Node*> Nodes; 
public: 
    void CreateNode(int Value) 
    { 
     Nodes.push_back(new Node(Value)); 
    } 
}; 

這顯示了基本對象工廠的技術,現在我的問題。 我可以向「NodeFactory」添加一個函數,例如「void SetType()」,並且能夠傳遞一個「Node」的子類,這個子類又會在「CreateNode」函數中創建子類?

非常感謝您的時間,我們非常感謝。

編輯:

的使用「空隙CreateNode()」被從終端用戶因此我的朝向好奇心「空隙RegisterType()」函數,其中用戶可以爲工廠註冊他們的子類抽象出來以創建而不是我提供的基礎類。

編輯:

更簡潔地表達出該問題將是如下:我怎樣才能讓用戶告訴該工廠創建它們的子類的實例,如果它們已經定義了一個,而不是我的默認基類?我想再次感謝大家花時間和精力回答這個問題。

+0

那麼,用戶應該如何使用這個工廠?你能舉一個想要的代碼的例子嗎?據我所知,他可以註冊一些type_,然後在一些算法或步驟期間,工廠開始根據註冊類型生成一些節點。我對麼? – zaufi

+0

如果'CreateNode()'旨在從用戶*中抽象出來,也許它不應該是'public'? – Praetorian

+0

正確,不應該。我創建了這個代碼來描述最簡單莊園中的問題。我應該把它改爲「SetType」,因爲工廠一次只能創建一種類型,「RegisterType」是誤導性的。 – KKlouzal

回答

2

我覺得這裏的問題是結合以下兩個要求:

  1. 您想使用相當簡單功能void CreateNode(int)創建任何節點。
  2. 您希望用戶能夠創建從節點派生的新節點並使用您的工廠創建它們。

現在我想提出的是類似於R Sahu的東西,但是沒有嚴格遵循工廠模式。

您可以通過要求用戶將小Creator對象傳遞給您的工廠來獲得所需的功能。 (請注意,這是偏離從傳統的工廠模式有點正如你基本上都採用作成類讓你NodeFactory成委託。)

class NodeCreator { 
public: 
    virtual Node* create(int) = 0; 
    virtual ~NodeCreator() = default; 
}; 

class DefaultNodeCreator : public NodeCreator { 
public: 
    virtual Node* create(int value) { 
     return new Node(value); 
    } 
}; 

現在我作爲一個用戶會創建自己的節點:

class MyNode : public Node { 
private: 
    int otherValue; 
public: 
    MyNode(int nodeValue, int otherValue) 
     : Node(nodeValue), otherValue(otherValue) 
    {} 
    // Implement other functionality... 
}; 

class MyNodeCreator : public NodeCreator {  
private: 
    // I added otherNodeValue to show that Creators can have a state. 
    int otherNodeValue; 
public: 
    MyNodeCreator(int otherNodeValue) : otherNodeValue(otherNodeValue) {} 
    virtual Node* create(int value) { 
     return new MyNode(value, otherNodeValue); 
    } 
}; 

現在終於在你的工廠類,你需要設置這樣的:

class NodeFactory 
{ 
    std::vector<Node*> Nodes; 
    std::unique_ptr<NodeCreator> activeCreator; 

public: 
    NodeFactory() { 
     setNodeCreator(nullptr); 
    } 

    void createNode(int Value) 
    { 
     Nodes.push_back(activeCreator->create(Value)); 
    } 

    void setNodeCreator(std::unique_ptr<NodeCreator> creator) { 
     if (creator == nullptr) { 
     activeCreator.reset(new DefaultNodeCreator()); 
     else { 
     activeCreator.reset(creator); 
     } 
    } 
}; 

要從主要用它:

int main() { 
    NodeFactory nf; 

    nf.createNode(1); // Creating Node(1) 
    nf.createNode(2); // Creating Node(2) 

    nf.setCreator(new MyNodeCreator(5)); 
    // Any nodes created now will be of type MyNode 
    // with otherNodeValue == 5. 

    nf.createNode(2); // Creating MyNode(2, 5) 
    nf.createNode(3); // Creating MyNode(3, 5) 
} 

最後請注意:

如果您打算讓您的用戶實現節點的子類,並使用這些具有多態性如上圖所示,這是你聲明節點的析構函數虛擬是非常重要的。你不能保證你的用戶不會在他們的子類中使用動態分配,所以你有責任確保他們的析構函數被調用。

+0

謝謝Stian,這表明了所需的功能。 – KKlouzal

0

你可能甚至不需要一個RegisterType() ...最簡單的方法是使用C++ 11(它可以讓你得到節點瓦特/不同的,比基本節點,構造函數簽名):

#include <iostream> 
#include <memory> 
#include <string> 
#include <type_traits> 
#include <vector> 

class Node 
{ 
    int SomeValue; 

public: 
    Node(int Value) 
     : SomeValue{Value} 
    { 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 

    // ATTENTION Make destructor virtual! 
    virtual ~Node() 
    { 
     // Cleanup the node 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 
}; 

class SomeOtherNode : public Node 
{ 
    std::string SomeStringValue; 

public: 
    SomeOtherNode(int Value, const std::string StringValue) 
     : Node{Value} 
     , SomeStringValue{StringValue} 
    { 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ~SomeOtherNode() 
    { 
     // Cleanup the string node 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 
}; 

class NotARealNode 
{ 
    int SomeValue; 

public: 
    NotARealNode(int Value) 
     : SomeValue{Value} 
    { 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ~NotARealNode() 
    { 
     // Cleanup the node 
     std::cout << __PRETTY_FUNCTION__ << std::endl; 
    } 
}; 

class NodeFactory 
{ 
    std::vector<std::unique_ptr<Node>> Nodes; 

public: 
    template <typename NodeType, typename... Args> 
    typename std::enable_if< 
     std::is_base_of<Node, NodeType>::value 
     >::type CreateNode(Args&&... args) 
    { 
     Nodes.push_back(
      std::unique_ptr<NodeType>{ 
       new NodeType{std::forward<Args>(args)...} 
       } 
     ); 
    } 
}; 

int main() 
{ 
    NodeFactory f; 
    f.CreateNode<Node>(123); 
    f.CreateNode<SomeOtherNode>(123, "Hello"); 
#if 0 
    // ATTENTION It wont compile, cuz NotARealNode is not a child of Node! 
    f.CreateNode<NotARealNode>(123); 
#endif 
    return 0; 
} 

輸出:

[email protected]>/work/tests> g++ -std=c++11 -o fff fff.cc 
[email protected]>/work/tests> ./fff 
Node::Node(int) 
Node::Node(int) 
SomeOtherNode::SomeOtherNode(int, std::string) 
virtual Node::~Node() 
virtual SomeOtherNode::~SomeOtherNode() 
virtual Node::~Node() 
+0

我不相信這會很有效,因爲「void CreateNode()」函數不是用戶自己應該或將要調用的東西。請參閱OP編輯。 :) – KKlouzal

0

你可以(?應該)使用多態了點。只需從NodeFactory派生(使CreateNode成爲一個虛函數)並讓它產生所需類型的節點。當然,你必須將Nodes矢量移動到不同的類中。

2

工廠模式旨在創建具有間接引用的對象。例如,用戶應該能夠調用:

Node* node = Factory::createNode("MyNodeType"); 

如果能夠創造這樣一個節點一個工廠,那麼函數的指針到MyNodeType對象返回。否則,它返回NULL。

爲了使此功能起作用,必須註冊一個Factory,它可以構造MyNodeType類型的對象。我們將不得不相信這樣的工廠會創建該類型的節點。

參與這一圖案中的類:

  1. 抽象基類Node
  2. 抽象基類Factory
  3. 具體子類Node調用MyNodeType
  4. 混凝土子類Factory。我們稱之爲MyNodeTypeFactory

這是一個這樣的骨架結構。

Node.h:

class Node 
{ 
    virtual ~Node() = 0; 
}; 

Factor.h:

#include <string> 

class Factory 
{ 
    public: 

     static void registerFactory(std::string const& productType, 
            Factory* factory); 

     static Node* creatNode(std::string const& productType); 

    private: 

     virtual Node* createNode(); 
}; 

Factory.cc:

#include <map> 

typedef std::map<std::string, Factory*> FactoryMap; 

static FactoryMap& getFactoryMap() 
{ 
    static FactoryMap factoryMap; 
    return factoryMap; 
} 

static void registerFactory(std::string const& productType, 
          Factory* factory) 
{ 
    getFactoryMap()[productType] = factory; 
} 

static Node* creatNode(std::string const& productType) 
{ 
    FactoryMap& factoryMap = getFactoryMap(); 
    FactoryMap::iterator iter = factoryMap.find(productType); 
    if (iter == factoryMap.end()) 
    { 
     // Unknown product. 
     return NULL; 
    } 

    return iter->second->createNode(); 
} 

MyNodeType.h:

#include "Node.h" 
class MyNodeType : public Node 
{ 
    MyNodeType() {} 
    virtual ~MyNodeType() {} 
}; 

MyNodeTypeFactory.h:

#include <Factory.h> 

class MyNodeTypeFactory : public Factory 
{ 
    public: 

     virtual Node* createNode(); 
}; 

MyNodeTypeFactory.cc:

#include "MyNodeTypeFactory.h" 

struct RegistrationHelper 
{ 
    MyNodeTypeFactorHelper() 
    { 
     Factory::registerFactory("MyNodeType", new MyNodeTypeFactory()); 
    } 
}; 

static RegistrationHelper helper; 

Node* MyNodeTypeFactory::createNode() 
{ 
    return MyNodeType(); 
} 
+0

這似乎是採取了正確的方法,但它似乎比我的方案中實際需要的做得多得多。我的程序只需要1個工廠,只生產一種類型的節點。工廠將生成我的默認基類,直到用戶派生出一個子類爲止,此時他們需要告訴工廠開始生成子類的對象而不是基類。我希望這是有道理的。感謝您的時間。 – KKlouzal

+0

@KKlouzal我認爲Stian V. Svedenborg的答案是您的最佳選擇。 –