Le contexte
J'ai utilisé avec une hiérarchie d'objets (une arborescence d'expression) un modèle de visiteur "pseudo" (pseudo, car il n'utilise pas la double répartition):
public interface MyInterface
{
void Accept(SomeClass operationClass);
}
public class MyImpl : MyInterface
{
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
}
Cette conception était cependant discutable, assez confortable car le nombre d'implémentations de MyInterface est important (~ 50 ou plus) et je n'avais pas besoin d'ajouter d'opérations supplémentaires.
Chaque implémentation est unique (c'est une expression ou un opérateur différent), et certains sont des composites (c'est-à-dire des nœuds d'opérateur qui contiendront d'autres nœuds d'opérateur / feuille).
La traversée est actuellement effectuée en appelant l'opération Accept sur le nœud racine de l'arborescence, qui à son tour appelle Accept sur chacun de ses nœuds enfants, qui à leur tour ... et ainsi de suite ...
Mais le moment est venu où j'ai besoin d' ajouter une nouvelle opération , comme une jolie impression:
public class MyImpl : MyInterface
{
// Property does not come from MyInterface
public string SomeProperty { get; set; }
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
public void Accept(SomePrettyPrinter printer)
{
printer.PrettyPrint(this.SomeProperty);
}
}
Je vois essentiellement deux options:
- Gardez le même design, en ajoutant une nouvelle méthode pour mon opération à chaque classe dérivée, au détriment de la maintenabilité (pas une option, à mon humble avis)
- Utilisez le "vrai" modèle de visiteur, au détriment de l'extensibilité (pas une option, car je m'attends à avoir plus d'implémentations à venir ...), avec environ 50+ surcharges de la méthode Visit, chacune correspondant à une implémentation spécifique ?
Question
Recommanderiez-vous d'utiliser le modèle Visitor? Y a-t-il un autre modèle qui pourrait aider à résoudre ce problème?
MyInterface
.. toutes ces classes ont-elles une implémentation unique deDoSomething
etDoSomethingElse
? Je ne vois pas où votre classe de visiteurs traverse réellement la hiérarchie - cela ressemble plus à unfacade
pour le moment ..Réponses:
J'ai utilisé le modèle de visiteur pour représenter des arbres d'expression au cours de plus de 10 ans sur six projets à grande échelle dans trois langages de programmation, et je suis très heureux du résultat. J'ai trouvé quelques choses qui ont rendu l'application du modèle beaucoup plus facile:
N'utilisez pas de surcharges dans l'interface du visiteur
Mettez le type dans le nom de la méthode, c'est-à-dire utilisez
plutôt que
Ajoutez une méthode "catch unknown" à votre interface visiteur.
Cela permettrait aux utilisateurs qui ne peuvent pas modifier votre code:
Cela leur permettrait de construire leurs propres implémentations de
IExpression
etIVisitor
qui "comprend" leurs expressions en utilisant des informations de type d'exécution dans l'implémentation de leurVisitExpression
méthode fourre-tout .Fournir une implémentation par défaut de l'
IVisitor
interfaceCela permettrait aux utilisateurs qui ont besoin de gérer un sous-ensemble de types d'expression de construire plus rapidement leurs visiteurs et de rendre leur code à l'abri de l'ajout de méthodes
IVisitor
. Par exemple, écrire un visiteur qui récolte tous les noms de variables de vos expressions devient une tâche facile, et le code ne se cassera pas même si vous ajoutez un tas de nouveaux types d'expression à votreIVisitor
plus tard.la source
Do not use overloads in the interface of the visitor
?