Skip to content

Latest commit

 

History

History
171 lines (103 loc) · 6.93 KB

File metadata and controls

171 lines (103 loc) · 6.93 KB

Strategy Design Pattern

Zurück


(Credits: Blog von Vishal Chovatiya)


Wesentliche Merkmale

Kategorie: Behavioral Pattern

Ziel / Absicht:

In einem Satz:

„Je nach Bedarf einen bestimmten Algorithmus aus einer Familie von Algorithmen auswählen.”

Das Strategie-Entwurfsmuster ist ein Entwurfsmuster, in dem eine Reihe ähnlicher Algorithmen definiert und jeweils in eigenen Klassen eingekapselt und implementiert werden. Der für einen bestimmten Zweck geeignete Algorithmus kann dann zur Laufzeit gemäß der vorliegenden Anforderungen ausgewählt und eingesetzt werden.

Problem:

Dieses Entwurfsmuster empfiehlt sich für Situationen, in denen sich Klassen nur im Verhalten (Behavior) unterscheiden. In diesem Fall sind die Algorithmen in separate Klassen zu unterteilen, die zur Laufzeit ausgewählt werden können. Insbesondere kapselt das Entwurfsmuster die Algorithmen und macht sie austauschbar.

Diese Trennung ermöglicht, dass das Verhalten (Methode, Algorithmus) unabhängig von den Clients ist, die es verwenden und auf diese Weise variieren kann. Eine Anwendung kann mit dem Fortschreiten der Anwendungsentwicklung weitere, neue Algorithmen nahtlos hinzuzufügen.

Lösung:

Das „Strategie Entwurfsmuster” zählt zur Kategorie der „Verhaltensmuster” / „Behavioral Pattern”. Es injiziert ein oder mehrere Verhaltensweisen in ein Objekt, die sich in ihrer Umgebung („Object / Context”) austauschen lassen.

Das Strategie Pattern wird häufig eingesetzt, um das Verhalten einer Klasse zu ändern, ohne dies über eine Spezialisierung vorzunehmen.

Struktur (UML):

Das folgende UML-Diagramm beschreibt eine Implementierung des Strategy Patterns. Es besteht im Wesentlichen aus drei Teilen:

  • Client: Diese Klasse verwendet austauschbare Algorithmen. Sie besitzt eine Referenz eines StrategyBase-Objekts.
  • StrategyBase: Definiert eine Schnittstelle, die allen unterstützten Algorithmen gemeinsam ist. Der Client verwendet diese Schnittstelle, um den über eine ConcreteStrategy-Schnittstelle definierten Algorithmus aufzurufen.
  • ConcreteStrategy: Konkrete Strategieklasse, die von der StrategyBase-Klasse erbt. Jede Instanz implementiert einen anderen Algorithmus, der (über die StrategyBase-Schnittstelle) vom Client verwendet werden kann.

Abbildung 1: Schematische Darstellung des Strategy Patterns.

Umsetzung des Patterns in einer Klassenhierarchie

Wir betrachten die Anwendung des Strategy Design Patterns in einer Hierarchie von Klassen.

In Abbildung 2 kann man zwei Hierarchien erkennen:

  • Eine Hierarchie von anwendungsorientierten Klassen. Hier werden in einer Vaterklasse zentrale Methoden virtuell definiert (z.B. Methode draw), diese sind in den Kindklassen geeignet zu überschreiben.

  • Die überschriebenen Methoden könnten eine Realisierung umsetzen, wollen dies aber vermeiden, um für unterschiedliche Technologien (Strategien) anpassbar zu sein. Dazu benötigt man eine zweite Hierarchie von Klassen, die unterschiedliche Implementierungen bereitstellen.

Abbildung 2: Anwendung des Strategy Design Patterns in einer Klassenhierarchie.

Umsetzung des Strategy Design Patterns in Modern C++

In einer Umsetzung des Strategy Design Patterns mit Modern C++ Sprachmitteln kommt das Klassentemplate std::function zum Einsatz. std::function ist ein universeller polymorpher Funktionswrapper, der zum Beispiel Lambda Objekte, freie Funktionen und andere aufrufbare Objekte kapseln kann.

Mit Hilfe von std::function fallen in dieser Variante alle Klassen weg, die mit Strategien zu tun haben.

Abbildung 3: Umsetzung des Strategy Design Patterns in Modern C++ mit std::function.

Einsatz des Strategy Design Pattern in der STL

  • std::sort: Im optionalen dritten Parameter kann ein Callable übergeben werden, dass die Strategie des Sortierens implementiert.

Hinweis:

Man kann in der Implementierung zwischen dynamischer und statischer Ausprägung unterscheiden. Die statische Implementierung basiert in C++ auf der Verwendung von Schablonen (template), die dynamische Implementierung auf dynamisch allokierten Strategy-Objekten (new), die zur Laufzeit austauschbar sind.


„Real-World” Beispiel

Im Verzeichnis ECommerceApp ist ein Beispiel gezeigt, dass Bezahlvorgänge in einer ECommerce-Anwendung auf Strategien umsetzt.


Statisches Strategy Design Pattern mit Template Klassen

Quellcode „Statische Shapes”

Das Decorator Design Pattern konnte man sowohl dynamisch als auch statisch implementieren, und dies kann man beim Strategy Design Pattern genauso umsetzen.

Im Prinzip gibt es nichts Besonderes an dieser Umsetzung zu beobachten, außer dass man den Algorithmus (hier im Beispiel: Methode add_list_item) nicht über die vtable anspricht (indirekter Methodenaufruf), sondern als C++–Template-Parameter übergibt. Dies bedeutet, dass der Algorithmus/die Strategie zur Laufzeit nicht änderbar ist. Der Methodenaufruf selbst erfolgt dann direkt, was eine verbesserte Laufzeit zur Folge hat!

Quellcode – Statische Umsetzung des Strategy Design Patterns (Templates)


Conceptual Example:

Quellcode 1 – Einfaches konzeptionelles Beispiel
Quellcode 2 – Einfaches konzeptionelles Beispiel mit std::function
Quellcode 3 – Einfaches konzeptionelles Beispiel mit einer Hierarchie von Klassen
Quellcode 4 – Einfaches konzeptionelles Beispiel mit einer Hierarchie von Klassen und std::function


Literaturhinweise

Die Anregungen zum konzeptionellen Beispiel finden Sie unter

https://refactoring.guru/design-patterns

und

https://www.codeproject.com

vor.

Das Beispiel zu der Auswahl der Bezahlungsmethode in einer E-Commerce App orierentiert sich an

https://refactoring.guru/design-patterns/strategy

Ein weiteres interessantes Beispiel zum Strategy Pattern widmet sich der Suche nach Knoten in einem gerichteten Graphen. Die Art der Suche (Depth First Search / DFS, Breadth First Search / BFS) wird als Strategie realisiert.


Zurück