Strategy Pattern
Das Strategy Pattern erlaubt es dir, verschiedene Algorithmen austauschbar zu machen - du kannst zur Laufzeit entscheiden welchen Algorithmus du verwendest.
Wie verschiedene Zahlungsmethoden: Kreditkarte, PayPal, Überweisung - alle machen das Gleiche (bezahlen), aber auf unterschiedliche Weise.
Grundidee
Das Strategy Pattern definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie austauschbar.
Problem ohne Strategy Pattern:
class Bezahlung {
void bezahlen(String methode, double betrag) {
if (methode.equals("Kreditkarte")) {
// Kreditkarten-Logik
System.out.println("Bezahle " + betrag + " mit Kreditkarte");
} else if (methode.equals("PayPal")) {
// PayPal-Logik
System.out.println("Bezahle " + betrag + " mit PayPal");
} else if (methode.equals("Überweisung")) {
// Überweisungs-Logik
System.out.println("Bezahle " + betrag + " per Überweisung");
}
}
}
Problem:
- Viele if-else Statements
- Schwer erweiterbar (neue Zahlungsmethode = Code ändern)
- Verletzt OOP-Prinzipien
Lösung mit Strategy Pattern
1. Strategy Interface
// Das Interface definiert die Strategie
interface BezahlStrategy {
void bezahlen(double betrag);
}
2. Konkrete Strategien
// Konkrete Strategie: Kreditkarte
class KreditkarteStrategy implements BezahlStrategy {
private String kartenNummer;
public KreditkarteStrategy(String kartenNummer) {
this.kartenNummer = kartenNummer;
}
public void bezahlen(double betrag) {
System.out.println("Bezahle " + betrag + " mit Kreditkarte " + kartenNummer);
}
}
// Konkrete Strategie: PayPal
class PayPalStrategy implements BezahlStrategy {
private String email;
public PayPalStrategy(String email) {
this.email = email;
}
public void bezahlen(double betrag) {
System.out.println("Bezahle " + betrag + " mit PayPal-Konto " + email);
}
}
// Konkrete Strategie: Überweisung
class ÜberweisungStrategy implements BezahlStrategy {
private String iban;
public ÜberweisungStrategy(String iban) {
this.iban = iban;
}
public void bezahlen(double betrag) {
System.out.println("Überweise " + betrag + " an IBAN " + iban);
}
}
3. Context-Klasse
class Warenkorb {
private BezahlStrategy bezahlStrategy;
// Setze Strategie zur Laufzeit
public void setBezahlStrategy(BezahlStrategy strategy) {
this.bezahlStrategy = strategy;
}
// Verwende aktuelle Strategie
public void checkout(double betrag) {
bezahlStrategy.bezahlen(betrag);
}
}
4. Verwendung
public class Main {
public static void main(String[] args) {
Warenkorb warenkorb = new Warenkorb();
// Kunde wählt Kreditkarte
warenkorb.setBezahlStrategy(
new KreditkarteStrategy("1234-5678-9012")
);
warenkorb.checkout(100.0); // Bezahle 100.0 mit Kreditkarte
// Kunde wechselt zu PayPal
warenkorb.setBezahlStrategy(
new PayPalStrategy("[email protected]")
);
warenkorb.checkout(50.0); // Bezahle 50.0 mit PayPal
}
}
UML-Diagramm
┌─────────────────┐
│ Warenkorb │
├─────────────────┤
│ - strategy │
├─────────────────┤
│ + setStrategy() │
│ + checkout() │
└────────┬────────┘
│
│ verwendet
▼
┌─────────────────────┐
│ <<interface>> │
│ BezahlStrategy │
├─────────────────────┤
│ + bezahlen(betrag) │
└──────────▲──────────┘
│
│ implementiert
┌─────┴─────┬─────────────┐
│ │ │
┌────┴────┐ ┌───┴────┐ ┌──────┴──────┐
│Kredit- │ │PayPal- │ │Überweisungs-│
│karte │ │Strategy│ │Strategy │
└─────────┘ └────────┘ └─────────────┘
Vorteile
- Austauschbarkeit - Algorithmen zur Laufzeit wechseln
- Erweiterbarkeit - Neue Strategien einfach hinzufügen
- Keine if-else Kaskaden - Sauberer Code
- Open-Closed Principle - Offen für Erweiterung, geschlossen für Änderung
- Testbarkeit - Jede Strategie einzeln testbar
Nachteile
- Mehr Klassen - Jede Strategie ist eine eigene Klasse
- Client muss Strategien kennen - Muss wissen welche es gibt
- Overhead - Für einfache Fälle eventuell zu komplex
Weitere Beispiele
Sortier-Strategien
interface SortStrategy {
void sort(int[] arr);
}
class BubbleSortStrategy implements SortStrategy {
public void sort(int[] arr) {
// Bubble Sort Implementierung
}
}
class QuickSortStrategy implements SortStrategy {
public void sort(int[] arr) {
// Quick Sort Implementierung
}
}
class Sortierer {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sortieren(int[] arr) {
strategy.sort(arr);
}
}
Verwendung:
Sortierer sortierer = new Sortierer();
// Für kleine Arrays
sortierer.setStrategy(new BubbleSortStrategy());
sortierer.sortieren(kleinesArray);
// Für große Arrays
sortierer.setStrategy(new QuickSortStrategy());
sortierer.sortieren(großesArray);
Kompressions-Strategien
interface KompressionsStrategy {
byte[] komprimiere(byte[] daten);
}
class ZipStrategy implements KompressionsStrategy {
public byte[] komprimiere(byte[] daten) {
// ZIP Kompression
return komprimierteDaten;
}
}
class GzipStrategy implements KompressionsStrategy {
public byte[] komprimiere(byte[] daten) {
// GZIP Kompression
return komprimierteDaten;
}
}
Wann Strategy Pattern nutzen?
- Du mehrere Varianten eines Algorithmus hast
- Du Algorithmen zur Laufzeit wechseln willst
- Du viele if-else oder switch-Statements vermeiden willst
- Algorithmen unabhängig vom Client sein sollen
- Du nur einen Algorithmus hast
- Strategien sich nie ändern
- Overhead zu groß für einfache Fälle
Unterschied zu anderen Patterns
Strategy vs Factory Pattern
| Aspekt | Strategy Pattern | Factory Pattern |
|---|---|---|
| Zweck | Algorithmus austauschbar machen | Objekte erstellen |
| Wann wählen | Zur Laufzeit | Bei Erstellung |
| Fokus | Verhalten | Erzeugung |
Strategy vs State Pattern
| Aspekt | Strategy Pattern | State Pattern |
|---|---|---|
| Zweck | Algorithmus wechseln | Zustand wechseln |
| Wechsel | Von außen | Automatisch intern |
| Beispiel | Zahlungsmethode wählen | Automaten-Zustände |
Prüfungsrelevanz AP2
- Definition erklären können
- UML-Diagramm zeichnen
- Vorteile/Nachteile nennen
- Beispiel-Code verstehen
- Wann einsetzen begründen
Typische Prüfungsfrage:
"Erkläre das Strategy Pattern anhand eines Beispiels und zeige den Unterschied zu einer if-else Lösung."
Verwandte Konzepte
- Design Patterns - Übergeordnetes Konzept
- Factory Pattern - Erzeugungsmuster
- Observer Pattern - Verhaltensmuster
- Singleton - Erzeugungsmuster
- OOP - Objektorientierte Programmierung
- Polymorphie - Basis-Konzept für Strategy
- Java Interface - Technische Umsetzung
Zusammenfassung
Das Strategy Pattern macht Algorithmen austauschbar, indem es sie in separate Klassen kapselt und über ein gemeinsames Interface zugänglich macht.
Merksatz:
"Verschiedene Wege führen zum Ziel - wähle den Weg zur Laufzeit!"