Was ist und wozu dient ein Factory-Pattern?

Manchmal kann es sinnvoll sein, die Bildung eines Objektes nicht direkt über den Konstruktoraufruf mit 'new' vorzunehmen, etwa wenn lange Parameterlisten oder eine Vielzahl an Konstruktoren vorliegen. In diesem Fall kann die Instanzierung über eine oder mehrere Factory- Methoden erfolgen.

Das Beispiel zeigt eine Klasse FahrzeugOrig, die zum Erstellen von Fahrzeug-Objekten wahlweise der Typen LKW oder PKW dienen soll. Die Klasse weist eine ganze Reihe verketteter Konstruktoren auf, um unterschiedliche Fahrzeug-Konfigurationen zuzulassen.
Ein solches Design ist unvorteilhaft, da die Auswahl des jeweils geeigneten Konstruktors unintuitiv ist und zu Fehlern führen kann. Einige Parameter kommen entweder nur für den Typ LKW (ladeflaeche, achsen, ladekran) oder nur für PKW (dach) in Frage. Durch die Verkettung der Konstruktoren muss somit ggf. null übergeben werden, was die Lesbarkeit des Codes verunklärt und fehlerträchtig ist. Wünschenswert wäre eine möglichst eindeutige Schnittstelle, die entweder PKW oder LKW erzeugt.

import java.awt.Color;
import java.awt.Rectangle;

public class Fahrzeug {

    public Fahrzeug(String type, Color color) {
        this(type, color, 2);
    }
    
    public Fahrzeug(String type, Color color, Dach dach) {
        this(type, color, 2, null, null, dach);
    }

    public Fahrzeug(String type, Color color, int achsen) {
        this(type, color, achsen, null);
    }

    public Fahrzeug(String type, Color color, int achsen,
            Rectangle ladeflaeche) {
        this(type, color, achsen, ladeflaeche, null, null);
    }

    public Fahrzeug(String type, Color color, int achsen, Rectangle ladeflaeche,
            Kran ladeKran, Dach dach) {
        System.out.println("Fahrzeug vom Typ " + type + " erzeugt");
    }
}

class Dach {
    // ...
}

class Kran {
    // ...
}

Selbstverständlich kann an dieser Stelle eingewandt werden, dass hier eine andere Klassenhierarchie angebracht wäre, etwa das Auslagern einer Klasse Fahrzeug in eine abstrakte Klasse, von der dann zwei KLassen LKW und PKW abgeleitet werden.
Gehen wir bei diesem Beispiel zu Demonstrationszwecken jedoch davon aus, dass es gewichtige Gründe gäbe, das Klassendesign selbst zu belassen. Wie lässt sich der Code dann verbessern?

Factory-Methoden sind solche, die die Erzeugung von Objekten kapseln. Im Beispiel werden zwei Factory-Methoden deklariert, die jeweils überladen sind. So wird beim Aufruf bereits deutlich, ob es sich beim neu gebildeten Objekt um ein PKW- oder ein LKW-Objekt handelt. Alle Methoden sind statisch deklariert und greifen auf lediglich einen privaten Konstruktor zu, der somit nicht mehr von außen aufgerufen werden kann. Die Konfiguration der jeweiligen Fahrzeug-Objekte wird innerhalb der Factory-Methoden durch Belegung der Parameter durchgeführt.

import java.awt.Color;
import java.awt.Rectangle;

public class Fahrzeug {

    private Fahrzeug(String type, Color color, int achsen,
            Rectangle ladeFlaeche, Kran ladeKran, Dach dach) {
        // ...
    }

    public static Fahrzeug createPKW(Color color) {
        return createPKW(color, null);
    }

    public static Fahrzeug createPKW(Color color, Dach dach) {
        return new Fahrzeug("PKW", color, 2, null, null, dach);
    }

    public static Fahrzeug createLKW(Color color, int achsen) {
        return createLKW(color, achsen, null);
    }

    public static Fahrzeug createLKW(Color color, int achsen,
            Rectangle ladeFlaeche) {
        return createLKW(color, achsen, ladeFlaeche, null, null);
    }

    public static Fahrzeug createLKW(Color color, int achsen, Rectangle
            ladeFlaeche, Kran ladeKran, Dach dach) {
        return new Fahrzeug("LKW", color, achsen, ladeFlaeche, ladeKran,
                null);
    }
}