Kontext
Ich habe mit einer Hierarchie von Objekten (einem Ausdrucksbaum) ein "Pseudo" -Besuchermuster verwendet (Pseudo, da darin kein doppelter Versand verwendet wird):
public interface MyInterface
{
void Accept(SomeClass operationClass);
}
public class MyImpl : MyInterface
{
public void Accept(SomeClass operationClass)
{
operationClass.DoSomething();
operationClass.DoSomethingElse();
// ... and so on ...
}
}
Dieses Design war jedoch fragwürdig und recht komfortabel, da die Anzahl der Implementierungen von MyInterface erheblich ist (~ 50 oder mehr) und ich keine zusätzlichen Operationen hinzufügen musste.
Jede Implementierung ist eindeutig (es ist ein anderer Ausdruck oder Operator), und einige sind Verbundwerkstoffe (dh Operatorknoten, die andere Operator- / Blattknoten enthalten).
Das Durchlaufen wird derzeit ausgeführt, indem die Accept-Operation auf dem Stammknoten des Baums aufgerufen wird, der wiederum Accept auf jedem seiner untergeordneten Knoten aufruft, was wiederum ... und so weiter ...
Aber es ist an der Zeit, dass ich einen neuen Vorgang hinzufügen muss , z. B. hübsches Drucken:
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);
}
}
Grundsätzlich sehe ich zwei Möglichkeiten:
- Behalten Sie das gleiche Design bei und fügen Sie jeder abgeleiteten Klasse auf Kosten der Wartbarkeit eine neue Methode für meine Operation hinzu (keine Option, IMHO).
- Verwenden Sie das "echte" Besuchermuster auf Kosten der Erweiterbarkeit (keine Option, da ich davon ausgehe, dass weitere Implementierungen auf dem Weg sind ...) mit mehr als 50 Überladungen der Visit-Methode, die jeweils einer bestimmten Implementierung entsprechen ?
Frage
Würden Sie die Verwendung des Besuchermusters empfehlen? Gibt es ein anderes Muster, das zur Lösung dieses Problems beitragen könnte?
MyInterface
.. Haben alle diese Klassen eine eindeutige Implementierung vonDoSomething
undDoSomethingElse
? Ich sehe nicht, wo Ihre Besucherklasse tatsächlich die Hierarchie durchquert - es siehtfacade
im Moment eher wie eine aus .Antworten:
Ich habe das Besuchermuster verwendet, um Ausdrucksbäume über einen Zeitraum von mehr als 10 Jahren in sechs Großprojekten in drei Programmiersprachen darzustellen, und ich bin sehr zufrieden mit dem Ergebnis. Ich habe ein paar Dinge gefunden, die das Anwenden des Musters viel einfacher gemacht haben:
Verwenden Sie keine Überlastungen in der Benutzeroberfläche des Besuchers
Geben Sie den Typ in den Methodennamen ein, dh verwenden Sie
eher, als
Fügen Sie Ihrer Besucheroberfläche eine "Fang unbekannt" -Methode hinzu.
Dies würde Benutzern ermöglichen, die Ihren Code nicht ändern können:
Auf diese Weise könnten sie ihre eigenen Implementierungen erstellen
IExpression
undIVisitor
ihre Ausdrücke "verstehen", indem sie Laufzeittypinformationen bei der Implementierung ihrer Catch-All-VisitExpression
Methode verwenden.Stellen Sie eine Standard-Do-Nothing-Implementierung der
IVisitor
Schnittstelle bereitAuf diese Weise können Benutzer, die sich mit einer Teilmenge von Ausdruckstypen befassen müssen, ihre Besucher schneller erstellen und ihren Code immun gegen das Hinzufügen weiterer Methoden machen
IVisitor
. Das Schreiben eines Besuchers, der alle Variablennamen aus Ihren Ausdrücken erntet, wird beispielsweise zu einer einfachen Aufgabe, und der Code wird nicht unterbrochen, selbst wenn Sie später eine Reihe neuer Ausdruckstypen hinzufügenIVisitor
.quelle
Do not use overloads in the interface of the visitor
?