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

Definition

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:

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

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

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?

Nutze Strategy Pattern wenn:

  • 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

Nutze es NICHT wenn:

  • 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

Für AP2 wichtig

  • 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

Zusammenfassung

Kern-Aussage

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!"