it-swarm.com.de

Vorteile des Strategiemusters

Warum ist es vorteilhaft, das Strategiemuster zu verwenden, wenn Sie Ihren Code nur in if/then-Fällen schreiben können?

Zum Beispiel: Ich habe eine TaxPayer-Klasse und eine ihrer Methoden berechnet die Steuern mit verschiedenen Algorithmen. Warum kann es also keine if/then-Fälle geben und herausfinden, welcher Algorithmus in dieser Methode verwendet werden soll, anstatt das Strategiemuster zu verwenden? Warum können Sie nicht einfach für jeden Algorithmus in der TaxPayer-Klasse eine separate Methode implementieren?

Was bedeutet es auch, wenn sich der Algorithmus zur Laufzeit ändert?

16
Armon Safai

Zum einen sind große Klumpen von if/else - Blöcken nicht leicht zu testen . Jeder neue "Zweig" fügt einen weiteren Ausführungspfad hinzu und erhöht somit die zyklomatische Komplexität . Wenn Sie Ihren Code gründlich testen möchten, müssen Sie alle Ausführungspfade abdecken, und für jede Bedingung müssen Sie mindestens einen weiteren Test schreiben (vorausgesetzt, Sie schreiben kleine, fokussierte Tests). Andererseits legen Klassen, die Strategien implementieren, normalerweise nur eine öffentliche Methode offen, die leicht zu testen ist.

Mit verschachteltem if/else Werden Sie am Ende viele Tests für einen einzelnen Teil Ihres Codes erhalten, während Sie mit Strategy nur wenige Tests für jede der mehreren einfacheren Strategien haben. Mit letzterem ist es einfach, eine bessere Abdeckung zu erzielen, da es schwieriger ist, Ausführungspfade zu übersehen.

Stellen Sie sich für Erweiterbarkeit vor, Sie schreiben ein Framework, in das Benutzer ihr eigenes Verhalten einbringen sollen. Sie möchten beispielsweise eine Art Steuerberechnungsrahmen erstellen und Steuersysteme verschiedener Länder unterstützen. Anstatt alle zu implementieren, möchten Sie den Framework-Benutzern lediglich die Möglichkeit geben, eine Implementierung zur Berechnung bestimmter Steuern bereitzustellen.

Hier ist das Strategiemuster:

  • Sie definieren eine Schnittstelle, z. TaxCalculation, und Ihr Framework akzeptiert Instanzen dieses Typs, um Steuern zu berechnen
  • Ein Benutzer des Frameworks erstellt eine Klasse, die diese Schnittstelle implementiert und an Ihr Framework übergibt. Auf diese Weise können Sie einen Teil der Berechnungen durchführen

Mit if/else Können Sie nicht dasselbe tun, da hierfür der Code des Frameworks geändert werden müsste. In diesem Fall wäre es kein Framework mehr. Da Frameworks häufig in kompilierter Form verteilt werden, ist dies möglicherweise die einzige Option.

Selbst wenn Sie nur normalen Code schreiben, ist Strategie von Vorteil, da sie Ihre Absichten klarer macht. Es heißt "diese Logik ist steckbar und bedingt", d. H. Es kann mehrere Implementierungen geben, die je nach Benutzeraktionen, Konfiguration oder sogar Plattform variieren können.

Die Verwendung des Strategiemusters kann die Lesbarkeit verbessern , da eine Klasse, die eine bestimmte Strategie implementiert, normalerweise einen beschreibenden Namen haben sollte, z. USAIncomeTaxCalculator, if/else Blöcke sind "namenlos", im besten Fall nur kommentiert, und Kommentare können lügen. Nach meinem persönlichen Geschmack ist es auch nicht lesbar, mehr als 3 if/else Blöcke hintereinander zu haben, und es wird mit verschachtelten Blöcken ziemlich schlecht.

Das Open/Closed-Prinzip ist ebenfalls sehr relevant, da Sie, wie im obigen Beispiel beschrieben, mit Strategy erweitern können eine Logik in einigen Teilen Ihres Codes ("zur Erweiterung öffnen"), ohne diese Teile neu zu schreiben ("zur Änderung geschlossen").

21
scriptin

Warum ist es vorteilhaft, das Strategiemuster zu verwenden, wenn Sie Ihren Code nur in if/then-Fällen schreiben können?

Manchmal sollten Sie nur if/then verwenden. Es ist einfacher Code, der leicht zu lesen ist.

Die beiden Hauptprobleme bei einfachem Wenn/Dann-Code sind, dass er gegen das offen geschlossenes Prinzip verstoßen kann. Wenn Sie jemals eine Bedingung hinzufügen oder ändern müssen, ändern Sie diesen Code. Wenn Sie mehr Bedingungen erwarten, ist das Hinzufügen einer neuen Strategie einfacher/sauberer/weniger wahrscheinlich.

Das andere Problem ist die Kopplung. Durch die Verwendung von if/then sind alle Implementierungen an diese Implementierung gebunden, sodass sie in Zukunft schwerer zu ändern sind. Bei Verwendung der Strategie besteht die einzige Kopplung an der Schnittstelle der Strategie.

5
Telastyn

Strategie ist nützlich, wenn if/then Bedingungen basieren auf Typen , wie in http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html erläutert

Typüberprüfungsbedingungen haben normalerweise keine hohe zyklomatische Komplexität, daher würde ich nicht sagen, dass die Strategie die Dinge dort notwendigerweise verbessert.

Der Hauptgrund für die Strategie wird im GoF-Buch S.316 erläutert, in dem das Muster eingeführt wurde:

Verwenden Sie das Strategiemuster, wenn

...

  • eine Klasse definiert viele Verhaltensweisen, und diese werden in ihren Operationen als mehrere bedingte Anweisungen angezeigt. Verschieben Sie verwandte bedingte Zweige anstelle vieler Bedingungen in ihre eigene Strategieklasse.

Wie in anderen Antworten erwähnt, ermöglicht das Strategiemuster bei angemessener Anwendung das Hinzufügen neuer Erweiterungen (konkreter Strategien), ohne dass notwendigerweise Änderungen am Rest des Codes erforderlich sind. Dies ist das sogenannte Open-Closed-Prinzip oder Protected Variations-Prinzip . Natürlich müssen Sie die neue konkrete Strategie noch codieren, und der Client-Code muss die Strategien als Plug-Ins erkennen (dies ist nicht trivial).

Mit if/then Bedingungen, es ist notwendig, den Code der Klasse zu ändern, die die bedingte Logik enthält. Wie in anderen Antworten erwähnt, ist dies manchmal in Ordnung, wenn Sie nicht die Komplexität hinzufügen möchten, um das Hinzufügen neuer Funktionen (Plug-Ins) ohne erneutes Kompilieren zu unterstützen.

4
Fuhrmanator

[...] wenn Sie Ihren Code nur in if/then-Fällen schreiben können?

Das ist genau der größte Vorteil des strategischen Musters. Keine Bedingungen haben.

Sie möchten, dass Ihre Klassen/Methoden/Funktionen so einfach und kurz wie möglich sind. Funktionscode ist sehr einfach zu testen und sehr einfach zu lesen.

Bedingungen (if/elseif/else) verlängern Ihre Klassen/Methoden/Funktionen, da sich normalerweise der Code, von dem eine Entscheidung true ergibt, von unterscheidet der Teil, in dem die Entscheidung false ergibt.


Ein weiterer großer Vorteil des Strategiemusters ist, dass es im gesamten Projekt wiederverwendbar ist.

Wenn Sie das Strategieentwurfsmuster verwenden, haben Sie sehr wahrscheinlich eine Art IoC-Container, aus dem Sie die gewünschte Implementierung einer Schnittstelle erhalten, möglicherweise durch eine getById(int id) -Methode, wobei id könnte ein Enumerator-Mitglied sein.

Dies bedeutet, dass die Erstellung der Implementierung nur an einer Stelle Ihres Codes erfolgt.

Wenn Sie weitere Implementierungen hinzufügen möchten, fügen Sie die neue Implementierung der Methode getById hinzu. Diese Änderung spiegelt sich überall im Code wider, in dem Sie sie aufrufen.

Mit if/elseif/else ist dies nicht möglich. Wenn Sie eine neue Implementierung hinzufügen, müssen Sie einen neuen elseif -Block hinzufügen und dies überall dort tun, wo die Implementierungen verwendet wurden. Andernfalls erhalten Sie möglicherweise einen ungültigen Code, weil Sie vergessen haben, die Implementierung zu ihrer hinzuzufügen Struktur.


Was bedeutet es auch, wenn sich der Algorithmus zur Laufzeit ändert?

In meinem Beispiel könnte id eine Variable sein, die basierend auf einer Benutzereingabe ausgefüllt wird. Wenn der Benutzer auf eine Schaltfläche A klickt, wird id = 2, wenn er auf eine Schaltfläche B klickt, dann id = 8.

Aufgrund des unterschiedlichen Werts id wird eine unterschiedliche Implementierung einer Schnittstelle aus dem IoC-Container abgerufen, und der Code führt unterschiedliche Operationen aus.

3
Andy

Warum ist es vorteilhaft, das Strategiemuster zu verwenden, wenn Sie Ihren Code nur in if/then-Fällen schreiben können?

Mit dem Strategiemuster können Sie Ihre Algorithmen (die Details) von Ihrer Geschäftslogik (übergeordnete Richtlinie) trennen. Diese beiden Dinge sind nicht nur verwirrend zu lesen, wenn sie gemischt werden, sondern haben auch sehr unterschiedliche Gründe, sich zu ändern.

Hier gibt es auch einen wichtigen Faktor für die Skalierbarkeit der Teamarbeit. Stellen Sie sich ein großes Programmierteam vor, in dem viele Leute an diesem Buchhaltungspaket arbeiten. Wenn sich die Steueralgorithmen alle in der Klasse oder dem Modul TaxPayer befinden, werden Zusammenführungskonflikte wahrscheinlich. Zusammenführungskonflikte sind zeitaufwändig und fehleranfällig zu lösen. Dieser Zeitverlust beeinträchtigt die Produktivität des Teams und die Fehler, die durch schlechte Zusammenführungen entstehen, beeinträchtigen die Glaubwürdigkeit der Kunden.

Was bedeutet es auch, wenn sich der Algorithmus zur Laufzeit ändert?

Ein Algorithmus, der sich zur Laufzeit ändert, wird durch Konfiguration oder Kontext bestimmt. Ein if/then Ansatz ermöglicht dies nicht effektiv, da vorhandene aktiv verwendete Klassen neu geladen werden müssen. Mit dem Strategie-Muster könnten die Strategie-Objekte, die jeden Algorithmus implementieren, bei Verwendung konstruiert werden. Infolgedessen können Änderungen an diesen Algorithmen (Fehlerkorrekturen oder Verbesserungen) zur Laufzeit vorgenommen und neu geladen werden. Dieser Ansatz könnte verwendet werden, um eine kontinuierliche Verfügbarkeit und keine Ausfallzeiten zu ermöglichen.

2
Alain O'Dea

An if/else An sich ist nichts auszusetzen. In vielen Fällen ist if/else Die einfachste und am besten lesbare Art, Logik auszudrücken. Der von Ihnen beschriebene Ansatz ist also in vielen Fällen vollkommen gültig. (Es ist auch perfekt testbar, so dass dies kein Problem ist.)

Es gibt jedoch einige besondere Fälle, in denen ein Strategiemuster die Wartbarkeit des Gesamtcodes verbessern kann. Zum Beispiel:

  • Wenn sich die jeweiligen Steuerberechnungsalgorithmen unabhängig voneinander und von der Kernlogik ändern können. In diesem Fall wäre es schön, wenn sie in verschiedene Klassen unterteilt würden, da Änderungen lokalisiert werden.
  • Wenn in Zukunft neue Algorithmen hinzugefügt werden können, ohne dass sich die Kernlogik ändert.
  • Wenn die Ursache für den Unterschied zwischen den beiden Algorithmen auch andere Teile des Codes betrifft. Angenommen, Sie wählen zwischen den beiden Algorithmen basierend auf der Einkommensklasse des Steuerzahlers. Wenn diese Einkommensklasse auch dazu führt, dass Sie andere Zweige an anderen Stellen im Code auswählen, ist es sauberer, eine der Einkommensklasse entsprechende Strategie einmal zu instanziieren und bei Bedarf aufzurufen, anstatt mehrere if/else-Zweige über den Code zu verteilen.

Damit das Strategiemuster Sinn macht, sollte die Schnittstelle zwischen der Kernlogik und den Steuerberechnungsalgorithmen stabiler sein als die einzelnen Komponenten. Wenn es genauso wahrscheinlich ist, dass eine Anforderungsänderung zu einer Änderung der Schnittstelle führt, ist das Strategiemuster möglicherweise tatsächlich eine Haftung.

Es kommt darauf an, ob die "Steuerberechnungsalgorithmen" sauber von der Kernlogik getrennt werden können, die sie aufruft. Ein Strategiemuster hat im Vergleich zu einem if/else Einen gewissen Overhead. Sie müssen also von Fall zu Fall entscheiden, ob sich die Investition lohnt.

1
JacquesB