0

I want to have a factory for the creation of handle object by the client, so I defined an interface for handle classes, but I am faced with the difficulty that the client have to initialize after creation of the handle object. The problem is that initialization depends on handle type.

The best I can do is the code below, but in this implementation, the client can initialize with the wrong data type. I try to deal with this problem using a template but I can't find a good solution.

How can I deal with this problem of initialization? Maybe a factory is not the best way?

#include <iostream>
#include <map>
#include <vector>
#include <string>

// type erasure for data type use in handle class
class HandleDataType
{
public:
    HandleDataType() : datatype("notype") {};
    HandleDataType(std::string s) : datatype(s) {};
public:
    std::string datatype;
};
class HandleDataTypeInt : public HandleDataType //simplify data type
{
    int data;
public:
    HandleDataTypeInt(int a) : data(a), HandleDataType("int")  {};
};
class HandleDataTypeDouble : public HandleDataType //simplify data type
{
    double data;
public:
    HandleDataTypeDouble(double a) : data(a),  HandleDataType("double")  {};
};

//class prototype 
template <class T>
class Prototype
{
public:
    virtual ~Prototype(){}  
    virtual T* Clone() const =0 ;
};

class HandleI : public  Prototype<HandleI>
{
    //Attribute
private:
    unsigned int        m_id;
    std::string         m_handletype;
    std::string         m_name;
    HandleDataType*     m_data;//m_data type depends of handleI subclass

public:
    //Constructor
    HandleI() {};
    HandleI(std::string type) : m_handletype(type) {};
    HandleI(std::string type, unsigned int id, std::string name) : m_handletype(type), m_id(id), m_name(name) {};
    ~HandleI() {};

    //get
    std::string     get_handletype() const {return m_handletype;};
    std::string     get_handlename() const {return m_name;};
    unsigned int    get_handleid()   const {return m_id;};
    HandleDataType* get_data()       const {return m_data;};

    // set
    void        set_name(const std::string name)    {m_name=name;};
    void        set_id(const unsigned int id)       {m_id=id;};
    void        set_data(HandleDataType & d)        {m_data=new HandleDataType(d);};

    //  virtual method if interface
public:
    virtual void            precompute() =0;
    virtual void            compute() =0;
};
// Handle Interface
class HandleInt : public HandleI
{
public:
    // constructor;
    HandleInt() : HandleI("HandleInt") {};
    HandleInt(unsigned int id, std::string name) : HandleI("HandleInt", id, name) {};
    ~HandleInt() {};

    // implementation of virtual method
public:
    // clone method from prototype for fabric
    HandleI*        Clone() const   {return new HandleInt(*this);}
    // inteface method
    void            precompute()    {std::cout<< "precomputeInt" << std::endl;}
    void            compute()       {std::cout<< "computeInt" << std::endl;}
};


class HandleDouble : public HandleI
{
public:
    // constructor;
    HandleDouble() : HandleI("HandleDouble") {};
    HandleDouble(unsigned int id, std::string name) : HandleI("HandleDouble", id, name) {};
    ~HandleDouble() {};

    // implementation of virtual method
public:         
    // clone method from prototype for fabric
    HandleI*        Clone() const   {return new HandleDouble(*this);}
    // inteface method
    void            precompute()    {std::cout<< "precomputeDouble" << std::endl;}
    void            compute()       {std::cout<< "computeDouble" << std::endl;}
};

class FactoryHandle
{
private :
    std::map<std::string, HandleI*> m_handleReference;

public :
    FactoryHandle()
    {
        m_handleReference["INT"] = new HandleInt() ;
        m_handleReference["DOUBLE"] = new HandleDouble() ;
    }
    HandleI* createHandle(std::string htype)
    {
        return m_handleReference[htype]->Clone();
    }
    ~FactoryHandle()
    {
        std::map<std::string, HandleI*>::iterator it;
        for (it=m_handleReference.begin(); it!=m_handleReference.end(); ++it)
            delete it->second;
        m_handleReference.clear();
    }
};

int main()
{
    HandleDataTypeInt * dataint = new HandleDataTypeInt(1);
    HandleDataTypeDouble * datadouble = new HandleDataTypeDouble(1.0);

    FactoryHandle* fh = new FactoryHandle() ;
    HandleI *   hint = fh->createHandle("INT");
    // initialise the handle hint
    hint->set_id(1);
    hint->set_name("test_int");
    hint->set_data(*dataint);
    //
    std::cout << hint->get_handletype() << std::endl;
    std::cout << hint->get_handleid() << std::endl;
    std::cout << hint->get_handlename() << std::endl;
    hint->compute();
    std::cout << hint->get_data()->datatype << std::endl;


    // but the client can also initialize with data of the wrong type like this
    //hint->set_data(*datadouble); -> i won't authorise such initalisation
    hint->set_id(1);
    hint->set_name("test_int");
    hint->set_data(*datadouble);
    //
    std::cout << hint->get_handletype() << std::endl;
    std::cout << hint->get_handleid() << std::endl;
    std::cout << hint->get_handlename() << std::endl;
    hint->compute();
    std::cout << hint->get_data()->datatype << std::endl; // we have here a double data type!!!
}
Thomas
  • 43,637
  • 12
  • 109
  • 140
wanjolivet
  • 91
  • 4
  • If you are implementing factory than please refer this link http://login2win.blogspot.in/2008/05/c-factory-pattern.html about factory that might be useful to you. – Hitesh Vaghani Jul 11 '13 at 09:58
  • I could try to answer this, but it will take ages. I think you could template the createHandle, and change your clone method to a create method that constructs the new object with the value. The clone method does not currently clone. I suspect you got downvoted before because this is a lot of code. – doctorlove Jul 11 '13 at 09:59
  • @HiteshVaghani I don't think the link shows how to initialise the data – doctorlove Jul 11 '13 at 10:04
  • Why not just make `set_data()` virtual and let each child class (like `HandleInt` and `HandleDouble`) check that the argument for their `set_data()` is the proper type. – Suedocode Jul 11 '13 at 15:11
  • If you're looking for a factory that also passes input values to the factory constructors, you may want to look at [this](http://stackoverflow.com/questions/17378961/elegant-way-to-implement-extensible-factories-in-c/17409442#17409442) post. You can map the factory based on strings (i.e. `"INT"` and `"DOUBLE"`) and require certain input types (i.e. `HandleDataTypeInt` and `HandleDataTypeDouble`). – Suedocode Jul 11 '13 at 15:20
  • @Aggieboy Your solution sounds good. I'new in OOP and i'm know that my problem is not well describe in my question but it seems you have proposed a good solution and i learn a lot to understand it. – wanjolivet Jul 11 '13 at 20:40
  • @wanjolivet consider answering your own question when you've sorted it :-) – doctorlove Jul 12 '13 at 08:07

0 Answers0