2009-05-21 9 views
1

J'ai une classe de base qui sous-classes dérivées héritent, il porte les fonctions de base qui devraient être les mêmes dans toutes les classes dérivées:Modèle de conception pour les fonctions optionnelles?

class Basic { 
public: 
    Run() { 
     int input = something->getsomething(); 
     switch(input) 
     { 
      /* Basic functionality */ 
      case 1: 
       doA(); 
       break; 
      case 2: 
       doB(); 
       break; 
      case 5: 
       Foo(); 
       break; 
     } 
    } 
}; 

Maintenant, en fonction de la classe dérivée, je veux « ajouter » plus déclarations de cas au commutateur. Quelles sont mes options ici? Je peux déclarer des fonctions virtuelles et ne les définir dans les classes dérivées qui vont les utiliser:

class Basic { 
protected: 
    virtual void DoSomethingElse(); 
public: 
    Run() { 
     int input = something->getsomething(); 
     switch(input) 
     { 
      /* Basic functionality */ 
      ... 

      case 6: 
       DoSomethingElse(); 
     } 
    } 
}; 


class Derived : public Basic { 
protected: 
    void DoSomethingElse() { ... } 
} 

Mais cela signifierait lors du changement des fonctions dans une classe dérivée, je dois modifier ma classe de base pour refléter les changements.

Existe-t-il un modèle de conception spécifique à ce type de problème? J'ai acheté un certain nombre de livres sur Design Patterns, mais je les étudie sur la base des besoins, donc je ne sais pas s'il y a un tel modèle que je cherche.

Répondre

4

Vous pouvez trouver utile de lire environ Chain of responsibility pattern et de repenser votre solution de cette façon.

Vous pouvez également déclarer 'doRun' comme méthode protégée et l'appeler dans le cas par défaut de la base.

default: 
    doRun(input); 

Et de définir doRun dans les classes dérivées.

Il en est ainsi appelé Template Method pattern

+0

Je pense que je vais aller pour le modèle de méthode de modèle pour l'instant. Je vais certainement lire et considérer les autres modèles listés ici, mais pour l'instant ceci peut être marqué comme accepté :) –

1

La procédure normale consiste à utiliser une usine. En résumé:

  • créer une hiérarchie de classes connexes qui fournissent la fonctionnalité.
  • créer une classe d'usine qui prend l'entrée et crée une instance du droit kindf de classe en fonction de l'entrée

Maintenant, pour les points bonus ajoutés:

  • créer un système qui reghisters cours withn l'usine - vous aurez besoin de spécifier l'entrée et le type de la classe à traiter

Maintenant, quand un besoin d'une nouvelle entrée vient, vous venez de dériver une nouvelle classe et l'enregistrer avec le usine. Le besoin de l'instruction switch disparaît.

+0

Je vais essayer de clarifier un peu plus mon raisonnement: je n'ai pas besoin de dériver en fonction de l'entrée: les classes dérivées ne vivront pas sur la pile. Je veux juste séparer les différents types de logique tout en conservant une logique «de base» pour toutes les classes dérivées. –

4

Je pense que le modèle dont vous avez besoin est chaîne de responsabilité ou peut-être stratégie combinée à une table d'appel dynamique ...

1

Mais cela signifierait lors du changement fonctions dans une classe dérivée, je devrait éditer ma classe de base à refléter ces changements.

Pourquoi cela serait-il vrai?

Je tiens compte de votre commentaire - alors si vous choisissez cette approche, vous aurez ces problèmes. D'autres ont posté des réponses qui suggèrent d'autres solutions - je vérifierais pour voir si elles vous aident.

+0

Eh bien peut-être que je l'ai mal formulé. Je n'ai pas à le faire quand je change le contenu d'une fonction, mais quand je supprime une fonction ou change son nom, je devrais retourner à la classe Basic pour refléter ces changements. En outre, je ne veux pas finir avec une classe 'Basic' qui a une tonne de fonctions virtuelles; un pour chaque fonction dans n'importe quelle classe qui en dérive. –

+0

c'est vrai seulement si la classe Basic a besoin des fonctions virtuelles ... est-ce que n'importe quel morceau de code appellera de telles fonctions sur les objets de la classe Basic ou est-ce juste pour un usage privé dans les sous-classes? Si le type des classes est connu pour chaque objet (c'est-à-dire que vous n'avez pas besoin de polymorphes, ce qui semble être le cas), vous n'avez pas besoin de déclarer les fonctions virtuelles dans la classe de base. – fortran

1

Si vos valeurs de sélection ne sont que de petits entiers, je remplacerais la déclaration de cas par une table de consultation. (Les actions dans le cas devront chacune être codées comme une fonction, ainsi vous pouvez mettre des pointeurs de fonction dans la table). Les classes héritées peuvent simplement ajouter des entrées à la table. (Je suppose que la table doit être une propriété d'instance, elle ne peut pas être statique).

Colin

+0

Je pensais utiliser des pointeurs de fonction, mais je suis relativement nouveau en C++ et je ne les ai pas encore étudiés assez profondément savoir comment les utiliser avec une utilisation variable des paramètres. –

1

solution simple А:

class Basic { 
    public: 
    void Run() { 
     const int input = ... 
     if (!(BaseProcess(input) || Process(input))) ... 
    } 

    vitual bool Process(int input) { return false; } 

    bool BaseProcess(int input) { 
     switch(input) { 
    ... 
     default: return false; 
     } 
     return true; 
    } 
... 

... et puis mettre en œuvre des cas supplémentaires dans la sous-classe Process(). Si vous devez prendre en charge plus de 2 niveaux (sous-sous-classe en ajoutant encore plus de cas), vous avez besoin d'une table de répartition dynamique.

Questions connexes