Zum Inhalt springen
Lektion 23 von 26 18 Min. Fortgeschritten Zur Übung Mit Codebeispiel

Design Patterns

Design Patterns sind erprobte Lösungen für wiederkehrende Entwurfsprobleme. Sie sind kein Selbstzweck, sondern ein gemeinsames Vokabular - und wenn eine einfache Methode reicht, ist ein Pattern zu viel.

Ansicht

01

Verstehen

Lies die Kernidee und das Beispiel.

02

Prüfen

Beantworte die Aktiv-Checks.

03

Anwenden

Setze die Transferaufgabe um.

Mini-Erfolg

Du hast Strategy, Factory und Observer in einem kleinen Beispiel erkannt und einen davon selbst gebaut.

Warum das wichtig ist: Patterns sind erprobte Lösungen für wiederkehrende Probleme. Wer sie kennt, versteht fremden Code schneller und diskutiert über Lösungen statt über Geschmack.

Das darf schwer sein

Das ist ein Fortgeschrittenen-Thema. Lies es einmal in Ruhe, baue das Mini-Projekt und komm später zurück. Du musst es nicht sofort können.

Lerninhalt

Verstehen & anwenden

1

Strategy: Verhalten austauschen

Strategy kapselt austauschbares Verhalten hinter einem Interface.

Statt Vererbung oder langer if-Ketten übergibst du dem Objekt die passende Strategie.

Weil funktionale Interfaces nur eine Methode haben, darf eine Strategie auch ein Lambda sein - praktisch für kurze Regeln.

StrategyKompositionVerhalten
Versandkosten als Strategie
interface Versand {
    double kosten(double gewicht);
}

class ExpressVersand implements Versand {
    public double kosten(double gewicht) {
        return 9.99 + gewicht * 0.5;
    }
}

class Warenkorb {
    private final Versand versand;

    Warenkorb(Versand versand) {
        this.versand = versand;
    }

    double versandkosten(double gewicht) {
        return versand.kosten(gewicht);
    }
}

// Als Lambda:
Versand pauschal = gewicht -> 4.99;
2

Factory: Erzeugung kapseln

Eine Factory entscheidet an einer Stelle, welche Implementierung entsteht.

Der Aufrufer kennt nur das Interface und muss die konkrete Klasse nicht importieren.

Das lohnt sich, wenn die Auswahl von Konfiguration, Plattform oder Eingaben abhängt. Für ein einzelnes new ist eine Factory meist zu viel.

Factory MethodErzeugungInterface
Zahlungsart auswählen
interface Zahlung {
    void bezahle(double betrag);
}

class ZahlungFactory {
    static Zahlung fuer(String art) {
        return switch (art) {
            case "karte" -> new KartenZahlung();
            case "paypal" -> new PayPalZahlung();
            default -> throw new IllegalArgumentException(
                "Unbekannte Zahlungsart: " + art);
        };
    }
}
3

Observer: Änderungen verteilen

Der Observer entkoppelt eine Quelle von beliebig vielen Empfängern.

Die Quelle kennt nur das Beobachter-Interface und ruft alle registrierten Beobachter bei einer Änderung auf.

In Java begegnet dir das Muster als Listener, zum Beispiel bei Events oder Property-Change-Listenern. Ein Consumer<String> reicht für ein kleines Beispiel.

ObserverListenerConsumer
Newsletter mit Beobachtern
import java.util.*;
import java.util.function.Consumer;

class Newsletter {
    private final List<Consumer<String>> abonnenten = new ArrayList<>();

    void abonnieren(Consumer<String> abonnent) {
        abonnenten.add(abonnent);
    }

    void veroeffentlichen(String text) {
        abonnenten.forEach(abonnent -> abonnent.accept(text));
    }
}

Newsletter news = new Newsletter();
news.abonnieren(text -> System.out.println("Mail: " + text));
news.abonnieren(text -> System.out.println("Push: " + text));

Vertiefung

Vom Problem zum Pattern

Ein Pattern ist keine Lösung, die man importiert. Du erkennst zuerst das Problem - zum Beispiel "mehrere Varianten desselben Verhaltens" - und wählst dann die kleinste Struktur, die es löst. Strategy nimmt Komposition, Factory nimmt die Erzeugung heraus, Observer verteilt Ereignisse.

Patterns haben Kosten: zusätzliche Klassen, Interfaces und Indirektion. Ein Singleton wird schnell zum globalen Zustand und erschwert Tests. Prüfe immer, ob eine einfache Methode oder ein Konstruktorparameter reicht.

Strategy und Factory kombiniert
interface Rabatt {
    double berechne(double summe);
}

static Rabatt rabattFuer(String art) {
    return switch (art) {
        case "treue" -> summe -> summe * 0.9;
        case "aktion" -> summe -> summe - 5;
        default -> summe -> summe;
    };
}

Merksatz: Wähle das Pattern nach dem Problem, nicht nach dem Katalog. Wenn die einfache Lösung lesbar bleibt, ist sie meist die bessere.

Übungsaufgabe

Benachrichtigungen mit Observer

Baue einen Newsletter, an den sich beliebig viele Beobachter anhängen können. Veröffentliche eine Nachricht und zeige, dass Mail- und Push-Ausgabe unabhängig reagieren.

Das soll dein Programm können

  • Interface Beobachter mit aktualisiere(String)
  • Newsletter verwaltet eine Liste von Beobachtern
  • Zwei Beobachter-Implementierungen
  • Newsletter kennt keine konkrete Ausgabe

Schreibe den Code hier oder in deiner IDE und vergleiche ihn dann mit der Lösung.

Dein Code wird automatisch auf diesem Gerät gespeichert.

Hinweis anzeigen

In veroeffentlichen reicht eine Schleife über die Liste und b.aktualisiere(nachricht). Die beiden Implementierungen geben unterschiedliche Texte aus.

Lösung anzeigen
Benachrichtigungen mit Observer.java
import java.util.*;

interface Beobachter {
    void aktualisiere(String nachricht);
}

class MailBeobachter implements Beobachter {
    public void aktualisiere(String nachricht) {
        System.out.println("Mail: " + nachricht);
    }
}

class PushBeobachter implements Beobachter {
    public void aktualisiere(String nachricht) {
        System.out.println("Push: " + nachricht);
    }
}

class Newsletter {
    private final List<Beobachter> beobachter = new ArrayList<>();

    void abonnieren(Beobachter b) {
        beobachter.add(b);
    }

    void veroeffentlichen(String nachricht) {
        for (Beobachter b : beobachter) {
            b.aktualisiere(nachricht);
        }
    }
}

public class NewsletterDemo {
    public static void main(String[] args) {
        Newsletter newsletter = new Newsletter();
        newsletter.abonnieren(new MailBeobachter());
        newsletter.abonnieren(new PushBeobachter());
        newsletter.veroeffentlichen("Neue Java-Lektion online");
    }
}

Warum funktioniert das?

Der Newsletter kennt nur das Interface und weiß nicht, ob eine Mail, ein Push oder ein Log entsteht. Ein neuer Kanal ist eine neue Klasse - die Quelle bleibt unverändert.

Aktiv-Check

Teste dein Verständnis

1/6

Erkennen: Welcher Abschnitt passt?

Welcher Abschnitt passt zu diesen Begriffen?

StrategyKompositionVerhalten
Tipp: Erst erinnern, dann prüfen. Fehler sind Teil des Lernens.

Spaßprojekt

Versand und Zahlung austauschbar

Baue einen kleinen Checkout mit austauschbaren Versandarten (Strategy) und einer Factory für Zahlungsarten. Ergänze eine neue Variante, ohne bestehenden Code zu ändern.

Selbstcheck

Kann ich das schon?

Hake ab, was du wirklich erklären oder selbst bauen könntest.

0 von 3 geschafft

Fortschritt

Bereit zum Abschließen?

Markiere die Lektion als abgeschlossen, um XP und Streak zu erhalten.

</> Learn-Program © 2026 · Lizenziert unter CC BY-NC-SA 4.0

Rechtliches & Lizenz