2010-06-28 8 views
0

Dans quelques projets récents auxquels j'ai participé j'ai été presque accro au modèle de codage suivant: (je ne suis pas sûr s'il y a un nom propre pour cela, mais de toute façon ...) Disons qu'un objet est dans un état déterminé et que nous ne voulons pas changer cet état de l'extérieur. Ces changements pourraient signifier n'importe quel comportement, pourrait invoquer des algorithmes, mais le fait est qu'ils se concentrent sur le changement de l'état (état membre, état des données, etc ...) de certains objets.Question de conception de programme C++

Et appelons un moyen discret de changer cet objet un Mutator. Mutators sont appliquées une fois (généralement) et ils ont une méthode interne comme apply(Target& target, ...), ce qui provoque instantanément le changement de l'état de l'objet (en fait, ils sont une sorte d'objets fonctionnels).

Ils pourraient aussi être facilement assimilés à des chaînes et appliqués un par un (Mutator m1, m2, ...); ils pourraient également dériver de certains BasicMutator base avec la méthode virtual void apply(...).

J'ai introduit des classes appelées InnerMutator et ExplicitMutator qui diffèrent en termes d'accès - premier d'entre eux peut aussi changer l'état interne de l'objet et doit être déclarée comme un ami (friend InnerMutator::access;).


Dans ces projets ma logique tournée travailler de la manière suivante:

  1. Préparer muteurs possibles, choisissez d'appliquer
  2. Créer et définissez le object à un état déterminé
  3. foreach (mutator) mutator.apply(object);

Maintenant la question.

Ce schéma turnes de bien travailler et (me) semble comme échantillon d'une certaine conception modèle non standard mais utile.

Ce qui me met mal à l'aise est que InnerMutator choses. Je ne pas pense que déclarer un mutateur un ami à chaque objet dont l'état pourrait être changé est une bonne idée et je ne veux pas trouver l'alternative appropriée.

cette situation pourrait être résolu en termes de Mutators ou pourriez-vous conseils un peu de modèle alternatif avec le même résultat?

Merci.

+0

Il pourrait être utile de déterminer si votre approche justifie ou non la façon un peu particulière de faire les choses si vous pouviez nous donner quelques exemples concrets. –

Répondre

5

J'espère que ce n'est pas pris offensivement, mais je ne peux pas m'empêcher de penser que cette conception est une solution trop sophistiquée pour un problème plutôt simple. Pourquoi avez-vous besoin d'agréger des mutateurs, par exemple? On peut simplement écrire:

template <class T> 
void mutate(T& t) 
{ 
    t.mutate1(...); 
    t.mutate2(...); 
    t.mutate3(...); 
} 

Il y a votre mutator total, seulement il est un modèle simple de fonction reposant sur le polymorphisme statique plutôt qu'une liste combinée de mutators faisant un super-mutator.

J'ai déjà fait quelque chose qui aurait pu être assez similaire. J'ai fait des objets de fonction qui pourraient être combinés par l'opérateur & & en objets super fonction qui appliquent les deux opérandes (dans l'ordre de gauche à droite), de sorte que l'on pourrait écrire du code comme:

foreach(v.begin(), v.end(), attack && burn && drain); 

Il était vraiment propre et semblait vraiment intelligent pour moi à l'époque et c'était assez efficace car il générait de nouveaux objets fonctionnels à la volée (pas de mécanismes d'exécution impliqués), mais quand mes collègues l'ont vu, ils pensaient que j'étais fou. Rétrospectivement, j'essayais de tout fourrer dans une programmation de style fonctionnel qui a son utilité mais qui ne devrait probablement pas être sur-utilisée en C++. Il aurait été assez facile à écrire:

BOOST_FOR_EACH(Something& something, v) 
{ 
    attack(something); 
    burn(something); 
    drain(something); 
} 

Accordée il est plus de lignes de code, mais il a l'avantage d'être centralisé et facile à déboguer et à ne pas introduire des concepts étrangers à d'autres personnes travaillant avec mon code. Je l'ai trouvé beaucoup plus bénéfique au lieu de se concentrer sur la logique serrée plutôt que d'essayer de réduire de force les lignes de code.

Je recommande que vous réfléchissiez profondément à cela et que vous envisagiez vraiment si cela vaut la peine de continuer avec cette conception basée sur des mutateurs, peu importe à quel point elle est intelligente et serrée. Si d'autres personnes ne peuvent pas le comprendre rapidement, à moins d'avoir l'autorité des auteurs de boost, il sera difficile de convaincre les gens de l'aimer. En ce qui concerne InnerMutator, je pense que vous devriez l'éviter comme la peste. Avoir des classes mutantes externes qui peuvent modifier les internes d'une classe aussi directement qu'elles le peuvent ici, va à l'encontre du but d'avoir des internes.

+0

+1: Mes pensées exactement sur les points mutateurs internes! –

3

Ne serait-il pas préférable de simplement faire ces Mutator s méthodes des classes qu'ils changent? S'il y a des fonctionnalités communes à plusieurs classes, alors pourquoi ne pas les faire hériter de la même classe de base? Faire ceci traitera de tout l'inconfort mutateur intérieur que j'imaginerais.

Si vous souhaitez mettre en file d'attente des modifications, vous pouvez les stocker en tant qu'ensembles d'appels de fonction aux méthodes dans les classes.

+0

Eh bien, mon schéma profite généralement au cas où vous voulez ajouter différents types de 'mutators' après et les définir en dehors de votre' object' (il pourrait s'agir de * stream filters *, par exemple). Je suppose que si je prends la façon que vous avez décrite, je devrais modifier la classe elle-même et semble que ce n'est pas la solution appropriée. –

+0

En fait, c'est la raison pour laquelle j'ai dû ajouter tout ce truc 'mutator' dans mon projet :) –

+0

Qu'est-ce qui vous empêche de revenir en arrière et d'ajouter la nouvelle fonctionnalité? Vous allez sûrement vous retrouver avec une solution plus propre si toutes les fonctions d'une classe qui modifient ses internes sont conservées avec la classe elle-même? –