2009-12-02 5 views
4

Supposons que j'aie une liste de classes A, B, C, ... qui héritent toutes de Base.Instancier des classes par leur nom avec un modèle d'usine

Je reçois le nom de classe sous la forme d'une chaîne de l'utilisateur, et je veux instancier la bonne classe et renvoyer un pointeur vers Base. Comment appliqueriez-vous cela?

J'ai pensé à utiliser une table de hachage avec le nom de classe comme clé, et un pointeur de fonction à une fonction qui instancie la bonne classe et retourne un Base *. Cependant, je pense que je pourrais peut-être utiliser le modèle d'usine ici et le rendre beaucoup plus facile, mais je ne me souviens pas très bien, alors je demanderais des suggestions.

+0

Votre idée avec une table de hachage avec des pointeurs de fonction sonore à peu près comme une usine pour moi. –

Répondre

7

Voici une factory example générique mise en œuvre:

template<class Interface, class KeyT=std::string> 
struct Factory { 
    typedef KeyT Key; 
    typedef std::auto_ptr<Interface> Type; 
    typedef Type (*Creator)(); 

    bool define(Key const& key, Creator v) { 
     // Define key -> v relationship, return whether this is a new key. 
     return _registry.insert(typename Registry::value_type(key, v)).second; 
    } 
    Type create(Key const& key) { 
     typename Registry::const_iterator i = _registry.find(key); 
     if (i == _registry.end()) { 
      throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) + 
             ": key not registered"); 
     } 
     else return i->second(); 
    } 

    template<class Base, class Actual> 
    static 
    std::auto_ptr<Base> create_func() { 
     return std::auto_ptr<Base>(new Actual()); 
    } 

private: 
    typedef std::map<Key, Creator> Registry; 
    Registry _registry; 
}; 

Ce ne vise pas à être le meilleur dans toutes les circonstances, mais il est destiné à être une première approximation et un défaut plus utile que la mise en œuvre manuellement le type de fonction stijn mentionné. La façon dont chaque hiérarchie doit s'inscrire n'est pas mandatée par Factory, mais vous pouvez aimer le method gf mentionné (c'est simple, clair, et très utile, et oui, cela surmonte les problèmes inhérents aux macros dans ce cas).

est ici un simple example de l'usine:

struct Base { 
    typedef ::Factory<Base> Factory; 
    virtual ~Base() {} 
    virtual int answer() const = 0; 

    static Factory::Type create(Factory::Key const& name) { 
     return _factory.create(name); 
    } 
    template<class Derived> 
    static void define(Factory::Key const& name) { 
     bool new_key = _factory.define(name, 
      &Factory::template create_func<Base, Derived>); 
     if (not new_key) { 
      throw std::logic_error(std::string(__PRETTY_FUNCTION__) + 
            ": name already registered"); 
     } 
    } 

private: 
    static Factory _factory; 
}; 
Base::Factory Base::_factory; 

struct A : Base { 
    virtual int answer() const { return 42; } 
}; 

int main() { 
    Base::define<A>("A"); 
    assert(Base::create("A")->answer() == 42); 
    return 0; 
} 
+0

Pourquoi le définissez-vous comme struct? Y a-t-il une raison? –

3

le moyen le plus rapide mais très utile dans beaucoup de domaines, serait quelque chose comme

Base* MyFactoryMethod(const std::string& sClass) const 
{ 
    if(sClass == "A") 
    return CreateNewA(); 
    else if(sClass == "B") 
    return new CreateClassB(); 
    //.... 
    return 0; 
} 

A* CreateClassA() const 
{ 
    return new A(); 
} 
+0

Ouais, je commence aussi habituellement par quelque chose de similaire - classe avec une méthode "create" similaire. Ensuite, s'il y a beaucoup de classes (ou que l'utilisateur de la bibliothèque est supposé en ajouter), j'ajoute la "vraie" usine derrière elle (carte, hash, peu importe) avec les inscriptions. – MaR

+0

peut-être que je devrais lire sur le modèle d'usine ... je ne me souviens pas comment il est mis en œuvre. – Idan

1

Tout d'abord, oui, c'est exactement ce que le modèle d'usine est pour.
(Soit dit en passant, votre autre idée est une mise en œuvre possible du modèle d'usine)

Si vous avez l'intention de le faire pour un grand projet (sinon, rendez-vous avec stijns answer), vous voudrez peut-être envisager en utilisant un conteneur associatif quelque part au lieu de ramification explicite et peut-être même déplacer la responsabilité de l'enregistrement dans les classes

  • éviter des changements de code dans un lieu (ou votre usine)
  • et à son tour, éviter p ossibly très longs temps de recompilation (pour en-tête-implémentations) lors de l'ajout d'une classe

Pour l'enregistrement pratique dans les classes que vous pouvez utiliser quelque chose comme this suggestion et ajouter un pointeur de fonction ou d'une functor aux entrées instancie la classe dérivée et renvoie un pointeur vers la base.
Si vous n'avez pas peur des macros, vous pouvez ajouter des classes à l'usine en ajoutant simplement une petite macro à sa déclaration.

2

Vous pouvez également regarder dans la mise en œuvre de la classe Boost factory.

  • S'il n'y a que quelques classes dérivées, vous pouvez utiliser une liste "if, else".
  • Si vous prévoyez d'avoir plusieurs classes dérivées, il est préférable de trier le processus d'inscription de classe (comme Georg mentionné) que d'utiliser une liste "if, else".

Voici un exemple simple en utilisant la méthode de l'usine de Boost et l'enregistrement de classe:

typedef boost::function<Parent*()> factory; 

// ... 

std::map<std::string, factory> factories; 

// Register derived classes 
factories["Child1"] = boost::factory<Child1*>(); 
factories["Child2"] = boost::factory<Child2*>(); 

// ... 

// Instantiate chosen derived class 
auto_ptr<Parent> pChild = auto_ptr<Parent>(factories["Child1"]()); 
+1

Salut, j'ai essayé cette solution mais je ne peux pas la faire compiler. Le problème que j'ai est à la ligne 'usines [" Child1 "] = boost :: factory ();' Il y a un problème typecheck, l'égalité n'est pas définie. Si j'écris 'boost :: factory ();' ça marche mais ce n'est pas utile. Ai-je manqué quelque chose? – Alexandre

+0

Hm mon erreur, je n'ai pas copié le code correctement ... – Alexandre

Questions connexes