Das Strategy-Muster eignet sich gut, um große if ... else-Konstrukte zu vermeiden und das Hinzufügen oder Ersetzen von Funktionen zu vereinfachen. Dennoch bleibt meiner Meinung nach ein Fehler. Es scheint, dass es in jeder Implementierung noch ein Verzweigungskonstrukt geben muss. Es kann sich um eine Fabrik oder eine Datendatei handeln. Ein Beispiel ist ein Bestellsystem.
Fabrik:
// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}
Der Code danach muss sich keine Sorgen machen, und es gibt nur einen Ort, an dem Sie jetzt einen neuen Auftragstyp hinzufügen können, aber dieser Codeabschnitt ist immer noch nicht erweiterbar. Das Herausziehen in eine Datendatei trägt etwas zur Lesbarkeit bei (fraglich, ich weiß):
<strategies>
<order type="NEW_ORDER">com.company.NewOrder</order>
<order type="CANCELLATION">com.company.Cancellation</order>
<order type="RETURN">com.company.Return</order>
</strategies>
Dies fügt jedoch noch zusätzlichen Code für die Verarbeitung der Datendatei hinzu - gewährter, einfacher zu testender und relativ stabiler Code, aber dennoch zusätzliche Komplexität.
Außerdem ist diese Art von Konstrukt nicht gut für den Integrationstest geeignet. Jede einzelne Strategie ist jetzt möglicherweise einfacher zu testen, aber jede neue Strategie, die Sie hinzufügen, ist eine zusätzliche zu testende Komplexität. Es ist weniger als Sie es hätten, wenn Sie das Muster nicht verwendet hätten, aber es ist immer noch da.
Gibt es eine Möglichkeit, das Strategiemuster zu implementieren, das diese Komplexität verringert? Oder ist das so einfach wie es nur geht und der Versuch, weiter zu gehen, würde nur eine weitere Abstraktionsebene für wenig oder gar keinen Nutzen hinzufügen?
quelle
eval
... könnte nicht in Java funktionieren, aber vielleicht in anderen Sprachen?Antworten:
Natürlich nicht. Selbst wenn Sie einen IoC-Container verwenden, müssen Sie irgendwo Bedingungen haben, die entscheiden, welche konkrete Implementierung injiziert werden soll. Dies ist die Natur des Strategiemusters.
Ich verstehe nicht wirklich, warum die Leute denken, dass dies ein Problem ist. In einigen Büchern wie Fowlers Refactoring heißt es , dass Sie, wenn Sie einen Schalter / Fall oder eine Kette von if / elses in der Mitte eines anderen Codes sehen, diesen als Geruch betrachten und versuchen sollten, ihn auf seine eigene Methode zu verschieben. Wenn der Code in jedem Fall mehr als eine Zeile, möglicherweise zwei, enthält, sollten Sie in Betracht ziehen, diese Methode zu einer Factory-Methode zu machen und Strategien zurückzugeben.
Einige Leute haben dies so verstanden, dass ein Schalter / Fall schlecht ist. Das ist nicht der Fall. Aber es sollte, wenn möglich, für sich bleiben.
quelle
Ja , indem Sie eine Hashmap / ein Wörterbuch verwenden, unter der sich jede Strategieimplementierung registriert. Die Fabrikmethode wird so etwas wie
Jede Strategieimplementierung muss die Factory mit ihrem orderType und einigen Informationen zum Erstellen einer Klasse registrieren.
Sie können den statischen Konstruktor für die Registrierung verwenden, wenn Ihre Sprache dies unterstützt.
Die register-Methode fügt lediglich der Hashmap einen neuen Wert hinzu:
[Update 2012-05-04]
Diese Lösung ist viel komplexer als die ursprüngliche "Switch-Lösung", die ich die meiste Zeit bevorzugen würde.
In einer Umgebung, in der sich Strategien häufig ändern (dh die Preisberechnung hängt vom Kunden, der Zeit usw. ab), kann diese Hashmap-Lösung in Kombination mit einem IoC-Container eine gute Lösung sein.
quelle
factory.register(NEW_ORDER, NewOrder.class);
sauberer oder weniger eine Verletzung von OCP als Reihen voncase NEW_ORDER: return new NewOrder();
?Bei "Strategie" geht es darum, mindestens einmal zwischen alternativen Algorithmen wählen zu müssen , nicht weniger. Irgendwo in Ihrem Programm muss jemand eine Entscheidung treffen - vielleicht der Benutzer oder Ihr Programm. Wenn Sie eine IoC verwenden, ändert Reflection, ein DataFile-Evaluator oder ein Switch / Case-Konstrukt nichts an der Situation.
quelle
Das Strategiemuster wird verwendet, wenn Sie mögliche Verhaltensweisen angeben, und eignet sich am besten zum Festlegen eines Handlers beim Start. Festlegen, welche Instanz der Strategie verwendet werden soll, kann über einen Mediator, Ihren Standard-IoC-Container, eine von Ihnen beschriebene Factory oder einfach durch Verwendung der richtigen, basierend auf dem Kontext erfolgen (da die Strategie häufig als Teil einer umfassenderen Verwendung bereitgestellt wird) der Klasse, die es enthält).
Für diese Art von Verhalten, bei dem verschiedene Methoden basierend auf Daten aufgerufen werden müssen, würde ich vorschlagen, die Polymorphismuskonstrukte zu verwenden, die von der jeweiligen Sprache bereitgestellt werden. nicht das Strategiemuster.
quelle
Im Gespräch mit anderen Entwicklern, bei denen ich arbeite, habe ich eine andere interessante Lösung gefunden - spezifisch für Java, aber ich bin sicher, dass die Idee in anderen Sprachen funktioniert. Konstruieren Sie eine Enumeration (oder eine Map, egal welche) mit den Klassenreferenzen und instanziieren Sie sie mit Reflection.
Dies reduziert den Werkscode drastisch:
(Bitte ignorieren Sie die schlechte Fehlerbehandlung - Beispielcode :))
Es ist nicht die meisten flexibel, wie ein Build noch erforderlich ist , ... aber es reduziert den Code Änderung einer Zeile. Mir gefällt, dass die Daten vom Werkscode getrennt sind. Sie können sogar Parameter festlegen, indem Sie entweder abstrakte Klassen / Schnittstellen verwenden, um eine allgemeine Methode bereitzustellen, oder Anmerkungen zur Kompilierungszeit erstellen, um bestimmte Konstruktorsignaturen zu erzwingen.
quelle
Die Erweiterbarkeit kann verbessert werden, indem für jede Klasse ein eigener Auftragstyp definiert wird. Dann wählt Ihre Fabrik die passende aus.
Beispielsweise:
quelle
Ich denke, es sollte eine Grenze geben, wie viele Branchen akzeptabel sind.
Wenn meine switch-Anweisung beispielsweise mehr als acht Fälle enthält, bewerte ich meinen Code neu und suche, was ich neu faktorisieren kann. Sehr oft stelle ich fest, dass bestimmte Fälle in einer separaten Fabrik zusammengefasst werden können. Ich gehe davon aus, dass es eine Fabrik gibt, die Strategien entwickelt.
Auf jeden Fall können Sie dies nicht vermeiden und müssen irgendwo den Status oder den Typ des Objekts überprüfen, mit dem Sie arbeiten. Sie werden dann eine Strategie dafür entwickeln. Gute alte Trennung von Bedenken.
quelle