W3docs

Einführung in Java Design Patterns

Eine Einführung in Design Patterns in Java — was sie sind und wie man die gebräuchlichsten verwendet.

Ein Design Pattern ist eine benannte, wiederverwendbare Lösung für ein Problem, das immer wieder beim Entwickeln von Software auftaucht. Patterns sind keine Bibliotheken, die man importiert, oder Code, den man kopiert — sie sind Schablonen für die Anordnung von Klassen und Objekten, auf die sich erfahrene Entwickler über die Zeit geeinigt haben. Sie zu kennen gibt dir ein gemeinsames Vokabular: Sag „wir verwenden hier eine Factory" und ein anderer Java-Entwickler weiß sofort ungefähr, was du meinst.

Diese Seite stellt vor, was Design Patterns sind, die drei Familien, in die sie fallen, und drei der gebräuchlichsten — Strategy, Factory und Singleton — mit einem ausführbaren Beispiel, das alle drei kombiniert. Es wird vorausgesetzt, dass du mit Interfaces und Polymorphismus vertraut bist, da fast jedes Pattern auf ihnen aufbaut.

Patterns wurden durch das Buch Design Patterns von 1994 der „Gang of Four" bekannt gemacht, das 23 davon katalogisierte. Du brauchst nicht alle 23 für den Einstieg. Eine Handvoll, zur richtigen Zeit eingesetzt, macht Code leichter änderbar, ohne ihn zu brechen.

Die drei Familien

Der klassische Katalog unterteilt Patterns in drei Gruppen nach dem, wobei sie helfen:

FamilieBereichBeispiele
CreationalWie Objekte erstellt werdenSingleton, Factory, Builder
StructuralWie Objekte zusammengesetzt werdenAdapter, Decorator, Facade
BehavioralWie Objekte interagierenStrategy, Observer, Iterator

Java selbst steckt voller solcher Patterns. StringBuilder ist ein Builder, Iterator ist das Iterator-Pattern und java.util.logging verwendet Singletons. Du hast Patterns bereits verwendet, ohne sie beim Namen zu nennen.

Strategy: den Algorithmus tauschen

Das Strategy-Pattern kapselt eine Familie austauschbarer Algorithmen hinter einem gemeinsamen Interface, sodass der aufrufende Code zwischen ihnen wechseln kann, ohne sich selbst zu ändern. Definiere das Interface, schreibe jede Variante als eigene Klasse und lass einen Kontext diejenige halten, die er gerade benötigt.

interface DiscountStrategy {
    double apply(double price);
}

class PercentOff implements DiscountStrategy {
    private final double percent;
    PercentOff(double percent) { this.percent = percent; }
    public double apply(double price) { return price * (1 - percent / 100); }
}

Eine Kontext-Klasse hält eine DiscountStrategy und delegiert an sie, anstatt eine if/switch-Kette zu verzweigen. Eine neue Rabattstrategie hinzuzufügen bedeutet, eine Klasse hinzuzufügen — nicht vorhandenen Code zu bearbeiten. (Der vollständige Checkout-Kontext sowie die Varianten NoDiscount und FlatOff erscheinen im ausführbaren Demo unten.)

Factory: Erstellung zentralisieren

Eine Factory ist eine einzelne Methode (oder Klasse), die dafür zuständig ist, zu entscheiden, welchen konkreten Typ sie instanziiert. Aufrufer fragen nach einem Objekt anhand einer Beschreibung und erhalten ein Objekt zurück, das das Interface erfüllt, ohne die genaue Klasse zu kennen.

static DiscountStrategy forCustomer(String tier) {
    return switch (tier) {
        case "gold"   -> new PercentOff(20);
        case "silver" -> new PercentOff(10);
        default       -> new NoDiscount();
    };
}

Die Erstellungslogik lebt an einem Ort. Wenn sich die Regeln ändern, bearbeitest du die Factory — jeder Aufrufer funktioniert weiterhin unverändert.

Singleton: genau eine Instanz

Ein Singleton garantiert, dass eine Klasse nur eine Instanz hat, und bietet einen globalen Zugriffspunkt darauf. Im modernen Java ist ein enum die einfachste, thread-sichere Methode, einen zu schreiben — die JVM garantiert, dass ihre Konstanten genau einmal erstellt werden. Siehe The Singleton Pattern für die Varianten mit verzögerter Initialisierung und Double-Checked-Locking.

enum Config {
    INSTANCE;
    private final String env = "production";
    public String env() { return env; }
}

// usage
String e = Config.INSTANCE.env();

Verwende Singletons sparsam — sie führen globalen Zustand ein, was das Testen erschwert. Oft ist ein einzelnes Objekt, das über einen Konstruktor übergeben wird (Dependency Injection), die bessere Wahl.

Wann man kein Pattern verwenden sollte

Patterns fügen Struktur hinzu, und Struktur hat ihren Preis. Ein switch mit zwei Fällen braucht das Strategy-Pattern nicht; eine Klasse, die man nur einmal erstellt, braucht keine Factory. Greife zu einem Pattern, wenn du den Schmerz spürst, den es löst — doppelte Verzweigungen, verstreute new-Aufrufe, verworrene Objektverdrahtung — nicht vorher. Zu häufige Anwendung von Patterns erzeugt Code, der schwerer zu lesen ist als das Problem, das er vereinfachen sollte.

Strategy und Factory zusammen

Das ausführbare Beispiel unten kombiniert zwei Patterns. DiscountStrategy ist das Strategy-Interface mit drei Implementierungen; forCustomer ist eine Factory, die eine davon auswählt. Der Checkout-Kontext delegiert an welche Strategie auch immer er hält, sodass sein Code sich nie ändert, während sich das Preisverhalten variiert.

java— editable, runs on the server

Was aus dem Programmablauf zu entnehmen ist:

  • Jede Stufe gibt ein anderes Ergebnis aus — regular bleibt bei $100.00, silver fällt auf $90.00, gold auf $80.00, coupon auf $95.00 — obwohl Checkout.total dieselbe einzelne Methode aufruft.
  • Der Checkout-Kontext verzweigt nie selbst nach der Stufe; er delegiert lediglich an die DiscountStrategy, die er gerade hält.
  • Die forCustomer-Factory ist der einzige Ort, der weiß, welche konkrete Klasse welcher Stufe entspricht, sodass die Auswahllogik an einem Ort lebt.
  • Das Verhalten zu tauschen ist zur Laufzeit einfach setStrategy(...) — einen vierten Rabatt hinzuzufügen würde eine neue Klasse und einen Fall in der Factory bedeuten, ohne Änderungen an Checkout.
  • Die letzten Zeilen bestätigen die aktive Strategie: nach erneuter Auswahl von gold liest die Police 20.0% off und die Summe beträgt $80.00, was beweist, dass der Kontext die zuletzt gesetzte Strategie widerspiegelt.

Übungsaufgaben

Übung
Welches Problem löst das Strategy-Pattern?
Welches Problem löst das Strategy-Pattern?
Was this page helpful?