KKlouzal
KKlouzal

Reputation: 722

Custom Object Factory

My library uses the Object Factory technique to create "Nodes" that have a specific purpose. By default these Nodes do their job but are quite basic. I want to allow the users of my library to be able and create a sub-class of the provided "Node" class to define their own functionality while keeping the base functionality of a node intact. Here is some example code to demonstrate my question:

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));
    }
};

This shows the basic Object Factory technique, now for my question. Can I add a function to the "NodeFactory" such as "void SetType()" and be able to pass in a sub-class of "Node" which in turn will have it create that sub-class during the "CreateNode" function?

Thank you very much for your time it is greatly appreciated.

EDIT:

The usage of "void CreateNode()" is abstracted away from the end user thus my curiosity towards a "void RegisterType()" function where the user can register their sub-class for the factory to create instead of the base-class I provide.

EDIT:

A more concise way to phrase the question would be as follows: How can I let the user tell the factory to create instances of their sub-class, if they've defined one, instead of my default base-class? I want to thank everyone again for their time and effort in answering this question.

Upvotes: 1

Views: 313

Answers (4)

Stian Svedenborg
Stian Svedenborg

Reputation: 1825

I think the problem here is combining the following two requirements:

  1. You want to use the quite simple function void CreateNode(int) to create any node.
  2. You want the user to be able to create new nodes derived from Node and use your factory to create them.

Now what I would suggest is something similar to R Sahu however without following the Factory pattern as strictly as he did.

You could get the functionality you seek by requiring your users to pass a small Creator-object to your factory. (Note that this is deviating a bit from the classical Factory-pattern. As you basically make your NodeFactory into a delegator using the creator-classes.)

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);
   }
};

Now I as a user will create my own node:

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);
   }
};

Now finally in your Factory class you need to set it like this:

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);
      }
   }
};

To use it from main:

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)
}

A final note:

If you intend for your users to implement subclasses of Node and use these with polymorphism as shown above, it is important that you declare Node's destructor as virtual. You have no guarantee that your users will not use dynamic allocation in their subclasses, so it is your responsibility to ensure that their destructors get called.

Upvotes: 2

R Sahu
R Sahu

Reputation: 206747

A Factory Pattern is meant to create objects with an indirect reference. For example, the user should be able call:

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

If there is a Factory that can create such a Node, then the function returns with a pointer to a MyNodeType object. Otherwise, it return NULL.

In order for this function to work, a Factory has to be registered that can construct objects of type MyNodeType. We will have to trust that such a Factory creates Nodes of that type.

The classes involved in this patter:

  1. The abstract base class Node.
  2. The abstract base class Factory.
  3. Concrete sub-class of Node called MyNodeType.
  4. Concreate sub-class of Factory. Let's call it MyNodeTypeFactory.

Here's such a skeletal structure.

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

Upvotes: 2

W.B.
W.B.

Reputation: 5525

You could (should?) use polymorphism for that. Just derive from NodeFactory (make CreateNode a virtual function) and have it spawn Nodes of your desired type. Of course you would have to move the Nodes vector into a different class.

Upvotes: 0

zaufi
zaufi

Reputation: 7129

You probably even don't need a RegisterType()... The simplest way is to use C++11 (it allows you to derive nodes w/ different, than a base Node, constructor signatures):

#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;
}

Output:

zaufi@gentop>/work/tests> g++ -std=c++11 -o fff fff.cc
zaufi@gentop>/work/tests> ./fff
Node::Node(int)
Node::Node(int)
SomeOtherNode::SomeOtherNode(int, std::string)
virtual Node::~Node()
virtual SomeOtherNode::~SomeOtherNode()
virtual Node::~Node()

Upvotes: 0

Related Questions