2009-06-06 5 views
2

Je suis à la recherche d'une solution de problème de conception de classe C++. Ce que j'essaye de réaliser est d'avoir la méthode de méthode statique dans la classe de base, qui retournerait des instances d'objets de types descendants. Le fait est que certains d'entre eux devraient être des singletons. Je l'écris en VCL donc il est possible d'utiliser __properties, mais je préfère les solutions C++ pures.Comment déclarer la méthode de type usine dans la classe de base?

class Base { 
    private: 
    static Base *Instance; 
    public: 
    static Base *New(void); 
    virtual bool isSingleton(void) = 0; 
} 
Base::Instance = NULL; 

class First : public Base { // singleton descendant 
    public: 
    bool isSingleton(void) { return true; } 
} 

class Second : public Base { // normal descendant 
    public: 
    bool isSingleton(void) { return false; } 
} 

Base *Base::New(void) { 
    if (isSingleton()) 
    if (Instance != NULL) 
     return Instance = new /* descendant constructor */; 
    else 
     return Instance; 
    else 
    return new /* descendant constructor */; 
} 

problèmes qui se posent:

  • comment déclarer la variable Instance statique, donc il serait statique dans les classes descendantes
  • comment appeler les constructeurs de descendants en classe de base

Je pense il pourrait être impossible de surmonter ces problèmes comme je l'avais prévu. Si oui, je voudrais un conseil sur la façon de le résoudre d'une autre manière.


Modifier: quelques modifications mineures dans le code. J'ai manqué quelques marques de pointeur dedans.

+0

vous voulez dire 'Instance == NULL'? – rriemann

Répondre

4

Juste pour vérifier que nous avons nos terminologies en phase - dans mon livre, une classe d'usine est une des instances de classe qui peuvent créer des instances d'une autre classe ou classes. Le choix du type d'instance à créer est basé sur les entrées que l'usine reçoit, ou au moins sur quelque chose qu'elle peut inspecter. est Heres une usine très simple:

class A { ~virtual A() {} }; 
class B : public A {}; 
class C : public A {}; 

class AFactory { 
    public: 
     A * Make(char c) { 
     if (c == 'B') { 
      return new B; 
     } 
     else if (c == 'C') { 
      return new C; 
     } 
     else { 
      throw "bad type"; 
     } 
     } 
}; 

Si je vous, je commencerais à nouveau, portant cet exemple et ceci à l'esprit:

  • factorioes ne doivent pas être singletons
  • usines ne pas doivent être membres statiques
  • usines ne doivent pas être membres de la classe de base pour les hiérarchies qu'ils créent
  • méthodes d'usine reviennent normalement un objet créé dynamiquement
  • méthodes d'usine retour normalement un pointeur
  • méthodes d'usine ont besoin d'un moyen de décider quelle classe pour créer une instance de

Je ne vois pas pourquoi votre usine a besoin d'une réflexion qui C++ ne saurait en aucun cas soutenir de manière significative.

+0

En utilisant l'usine typique, comme celle que vous avez fournie, je devrais ajouter du code à la méthode Make() chaque fois que j'ajouterai une nouvelle classe descendante. C'est ce que je considère redondant et nuisible, car je veux fournir à ma classe de base un cadre fermé. – samuil

+3

Non, vous ne le faites pas. Vous pouvez enregistrer de nouvelles classes avec l'usine sans changer le code d'usine. C'est un modèle très commun - avez-vous lu un bon livre sur ce sujet? –

+0

@samuil: Pensez à utiliser une usine abstraite dans laquelle vous enregistrez des usines concrètes à l'exécution. Vous codez votre usine abstraite une seule fois (elle permet l'enregistrement des usines de béton) et vous créez une nouvelle usine de béton pour chaque nouveau type que vous voulez que l'usine abstraite crée. C'est un modèle assez commun. –

0

Que diriez-vous quelque chose comme ceci:

class Base 
{ 
public: 
virtual Base construct() = 0; 
}; 

class First : public Base 
{ 
public: 
Base construct(){ return First(); // or whatever constructor } 
}; 

class Second : public Base 
{ 
public: 
Base construct(){ return Second(...); // constructor } 
}; 
+0

J'utilise cette méthode pour l'instant, mais je dois écrire la méthode construct() dans chaque descendant - je la considère redondante, comme c'est évident ce qu'elle doit faire. – samuil

+0

Je pensais que la recommandation habituelle était d'appeler vos méthodes 'factory' 'create()' vu qu'elles sont très spécifiquement des constructeurs _not_. –

2

Baser cela sur la réponse par @Shakedown, je vais faire Base être le type basé sur un modèle réel, en utilisant la CRTP:

template <class T> 
class Base 
{ 
public: 
static std::auto_ptr<Base<T> > construct() 
{ 
    return new T(); 
} 
}; 

class First : public Base<First> 
{ 
}; 

class Second : public Base<Second> 
{ 
}; 

C'est agréable car construct est maintenant encore une fois un membre statique. Vous l'appelez comme:

std::auto_ptr<First> first(First::construct()); 
std::auto_ptr<Second> second(Second::construct()); 

// do something with first and second... 
+0

Approche intéressante. J'essaie actuellement de voir si cela correspond à mes besoins, et de remplacer le modèle d'usine typique mentionné par Neil Butterworth. – samuil

1

Vous pouvez créer une classe Singleton et une classe NonSingleton, et de faire tous les descendants héritent l'un d'eux.

class Base { 
    public: 
    static Base *New() = 0; 
} 

class SingletonDescendant: public Base { 
    public: 
    *Base::New() { 
     if (Instance != NULL) 
     return Instance = new /* descendant constructor */; 
     else 
     return Instance; 
    } 
    private: 
    static Base *Instance; 
} 
SingletonDescendant::Instance = NULL; 

class NonSingletonDescendant: public Base { 
    public: 
    *Base::New() { 
     return new; 
    } 
} 

class First : public SingletonDescendant{ // singleton descendant 
} 

class Second : public NonSingletonDescendant{ // normal descendant 
} 

Il permet de résoudre les questions que vous avez soulevées:

  • Comment déclarer instance variable statique, donc il serait statique dans les classes descendantes: Il existe seulement dans la classe SingletonDescendant.
  • Comment appeler les constructeurs de descendants en classe de base: En utilisant la nouvelle fonction
  • Je dois écrire construct() méthode dans tous les descendants; Je le considère redondant, comme il est évident ce qu'il a à faire: Maintenant, il est seulement dans SingletonDescendant et NonSingletonDescendant.
+0

La distinction entre les descendants singleton et non-singleton semble bonne, mais vous avez quitté mon/* constructeur descendant commenté * /. Pour cette raison, je devrais implémenter une nouvelle méthode pour chaque descendant de toute façon. – samuil

Questions connexes