Ein guter Plan! Hmm, oder der? Oder doch der andere?

Hallo Spaß-Coder.

Mit einem weiteren Thema aus unserer Reihe über Entwurfsmuster geht es nun munter weiter. Wir werden uns nun einmal mit einem Mustern aus der Kategorie der Verhaltensmuster beschäftigen. Wir schauen uns in diesem Artikel das Strategie-Muster (engl. Strategy Pattern) an, ebenfalls aus der Sammlung der Gang of Four.

 

Was ist denn das, eine Strategie?

Der Begriff der Strategie stammt aus der Kriegsführung und bezeichnet laut Duden einen

genaue[n] Plan des eigenen Vorgehens, der dazu dient, ein militärisches, politisches, psychologisches, wirtschaftliches o. ä. Ziel zu erreichen, […]

Dabei werden unterschiedliche Fakten in die Entscheidung für ein Vorgehen einbezogen. Diese Fakten können natürlich zu unterschiedlichen Vorgehen führen, weshalb es ratsam ist, für unterschiedliche Situationen unterschiedliche Strategien in petto zu haben.

Was bedeuten diese Erkenntnisse nun für die Softwareentwicklung? Was bedeutet es für unsere Architektur?

Um auf unterschiedliche Situationen (Anforderungen) reagieren zu können, können wir das Strategie-Muster nutzen. Dazu übergeben wir unserer Klasse einfach den Teil der Implementierung, der sich verändern kann. Hierbei folgen wir dem Open-Closed- und dem Single-Responsibility-Prinzip.

Die übergebenen Strategien müssen dabei dieselbe Schnittstelle implementieren, damit wir sie nutzen können.

 

Wie funktioniert das Strategie-Muster?

Beim Strategie-Muster wird einer Klasse ein Objekt übergeben, welche das Verhalten der Klasse beeinflusst. Durch dieses injizierte Verhalten muss die eigentliche Klasse nicht mehr alle Entscheidungen treffen, sondern kann die Anforderungen, die von außen gestellt werden, direkt unterstützen. Das Verhalten wird also von außen vorgegeben.

 

Das ist doch alles nur ein Spiel

Nehmen wir als Beispiel noch einmal unser altbekanntes Tic-Tac-Toe.

Die Idee ist es, das Spiel dahingehend flexibel zu gestalten, welche Art von Spieler am Spiel teilnimmt. So kann es ein menschlicher Spieler sein, aber auch eine KI, eine künstliche Intelligenz. Diese wiederum kann unterschiedlich implementiert werden. Vielleicht auf Basis von Zufall? Oder nach dem Minimax-Algorithmus? Hier sind die unterschiedlichsten Implementierungen denkbar.

Machen wir für die zwei der drei genannten mal eine konkrete Implementierung.

Aus Sicht des Clients ist nur eine Frage interessant: Wo in unserem 3×3-Gitter soll das nächste Symbol (also X oder O) gesetzt werden. Als Basis für diese Entscheidung erhält die Strategie den aktuellen Status des Spiels.

Das dazugehörige Interface sieht entsprechend einfach aus:

Schauen wir uns nun einmal die Implementierung für einen menschlichen Spieler an, der über die Konsole seine Eingaben macht:

Es werden also so lange Zahlen zwischen 1-9 über die Konsole abgefragt, bis ein gültiger Wert für einen Zug eingegeben wurde. Dieser wird dann an den Client zurückgegeben. Diese menschliche Strategie implementiert unser Interface.

Als nächstes schauen wir uns eine KI-Implementierung an, die das Symbol an eine zufällige Stelle im Spielfeld setzt:

Auch diese ist von der Implementierung her denkbar einfach. Es werden zufällig Zahlen ermittelt, die auf Gültigkeit geprüft und dann zurückgegeben werden. Diese KI-Strategie implementiert ebenfalls unser Interface.

Wie aber nutzen wir diese nun? Auch das ist ziemlich einfach:

Wir erstellen also unsere Spieler und übergeben diesen – neben dem Namen und dem zu verwendenden Symbol – jeweils die passende Strategie.

Im Spieler sieht das dann folgendermaßen aus:

In der Methode getNextMove() des jeweiligen Spielers wird nun die entsprechende Strategie verwendet, die für den Spieler übergeben wurde. Einfach, oder?

An der Klasse Spieler müssen wir also nichts ändern, wenn wir eine neue Zug-Strategie implementieren möchten.

 

Wann ist das Strategie-Muster sinnvoll anzuwenden?

Die Verwendung des Strategie-Musters bietet sich insbesondere dann an, wenn wir erst zur Laufzeit genau wissen, was wir eigentlich tun möchten (etwa durch eine Konfiguration oder eine Laufzeitbedingung festgelegt).
Auch wenn wir unterschiedliche (austauschbare) Varianten eines Algorithmus nutzen möchten, bietet es sich an, diesen als Strategie an die verarbeitende Klasse zu übergeben, statt alles doppelt zu implementieren.

Wollen wir Wiederverwendbare Klassen schaffen, ist das Strategie-Muster oft eine gute, leicht zu implementierende Lösung. Ein Code-Smell, bei dem man überlegen könnte, das Strategie-Muster anzuwenden sind eine große Anzahl von Verzweigungen im Code (if-else-if-else-if, switch-case). Statt die Entscheidung in der implementierenden Klasse zu treffen, kann der Ausführungsteil einfach übergeben werden. Die Entscheidung erfolgt dann außerhalb, was die Klasse selbst vor Veränderungen schützt.

 

Zusammenfassung?

Mit dem Strategie-Muster steht uns ein mächtiges, leicht zu verstehendes und leicht zu implementierendes Muster zur Verfügung um unseren Code sauber zu machen und Abhängigkeiten klar voneinander zu trennen. Eine Strategie hilft bei der Umsetzung der meisten SOLID-Prinzipien.

Lediglich die Auswahl der richtigen Strategie zur Laufzeit kann zu einem Problem führen, das diese Aufgabe nun in den Client verlagert wird. Wichtig ist auch, dass man sich beim Einsatz einer Vielzahl von Strategien für ein gut strukturiertes Ablage- und Namensschema entscheidet und dies auch einhält. Sonst kann der Überblick schnell verloren gehen.

Fällt euch spontan eine Stelle ein, bei der euch das Muster hätte helfen können? Wer mag, kann ja auch mal eine KI-Strategie nach dem Minimax-Algorithmus implementieren. Eine Musterlösung gibt es unter Github (siehe unten).

 

Wir wünschen euch viel Spaß beim Ausprobieren der unterschiedlichen Strategien.

Eure Spaß-Coder

 

Dieser Artikel basiert neben unseren Erfahrungen auf den Ausführungen aus:

  • http://www.duden.de/rechtschreibung/Strategie
  • https://de.wikipedia.org/wiki/Strategie_%28Entwurfsmuster%29
  • https://dzone.com/articles/design-patterns-strategy

Code-Beispiele auf Github:

  • https://github.com/invidit/CodeQuality/tree/master/TicTacToe

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.