Fabrikmethode
Einführung
Stell dir vor, du arbeitest in einem Projekt mit vielen unterschiedlichen Objekten – zum Beispiel Fahrzeugen, Benachrichtigungen oder Spielfiguren. Jedes Mal, wenn ein neuer Typ hinzukommt, musst du den bestehenden Code anpassen.

Das kostet Zeit, macht den Code unübersichtlich und erhöht das Fehlerrisiko.
Um diese Probleme zu vermeiden, brauchst du ein Muster, das die Objekterstellung vom restlichen Code entkoppelt und flexibel gestaltet. Genau das bietet dir das Factory Method Pattern. In dieser Einheit erfährst du, wie es funktioniert und warum es deine Softwareentwicklung deutlich erleichtert.
Überleitung
Nach dieser Lerneinheit kannst du:
-
Das Factory Method Pattern definieren und den Zweck erklären. Du kannst beschreiben, wie das Pattern funktioniert und warum es zur Kategorie der Erzeugungsmuster gehört.
-
Die Struktur des Patterns nachvollziehen. Du erkennst die Rollen von Produkt-Interface, konkreten Produkten, Factory-Klasse und Client.
-
Vor- und Nachteile benennen und bewerten. Du kannst erklären, wann der Einsatz sinnvoll ist und wann es sich um unnötige Komplexität handelt.
-
Das Pattern auf praktische Szenarien anwenden. Du bist in der Lage, anhand von Beispielen wie Fahrzeugerstellung oder Notification-System eigene Factory-Implementierungen zu entwickeln.
Überleitung
Das Factory Method Pattern (auf Deutsch: Fabrikmethode) ist ein Entwurfsmuster aus der Softwareentwicklung. Es gehört zur Kategorie der Erzeugungsmuster und hat das Ziel, die Objekterstellung flexibel und erweiterbar zu gestalten.
Schauen wir uns das Problem ohne Factory Method an
Problemstellung ohne Factory Method
Wenn du eine Anwendung entwickelst und unterschiedliche Objekte erzeugen musst, zum Beispiel Charaktere in einem Spiel, sieht der naive Ansatz oft so aus:
public class SpielClient {
public void starteSpiel() {
Charakter held;
if (spielerWahl.equals("Krieger")) {
held = new Krieger("Stark", 100, 80);
} else if (spielerWahl.equals("Magier")) {
held = new Magier("Weise", 60, 120);
} else if (spielerWahl.equals("Bogenschütze")) {
held = new Bogenschuetze("Schnell", 70, 90);
}
// Problem: Was passiert bei neuen Charaktertypen?
}
}Erklärung des Codes
Dieser Code zeigt ein Problem bei der Objekterzeugung (dem “Bauen” von Objekten), wenn diese von einer Benutzereingabe (spielerWahl) abhängt.
SpielClient: Dies ist eine Klasse, die das Spiel steuert.starteSpiel(): Diese Methode startet das Spiel und muss den Charakter (Held) des Spielers erstellen.if/elif-Kette: Die Methode prüft die VariablespielerWahl(die hier nicht definiert ist, aber von außen kommen würde).- Je nachdem, was der Spieler gewählt hat (“Krieger”, “Magier” etc.), wird explizit ein neues Objekt der jeweiligen Klasse (
new Krieger(...),new Magier(...)) erstellt und in derheld-Variable gespeichert.
- Je nachdem, was der Spieler gewählt hat (“Krieger”, “Magier” etc.), wird explizit ein neues Objekt der jeweiligen Klasse (
- Das Problem (im Kommentar): Die
starteSpiel-Methode ist stark an die konkreten Charakterklassen gekoppelt. Wenn ein neuer Charaktertyp (z.B. “Schurke”) hinzugefügt wird, muss dieseif/elif-Kette imSpielClientgeändert werden. Das macht den Code schwer wartbar und verstößt gegen das Open/Closed-Prinzip (offen für Erweiterung, geschlossen für Änderung).
Die Lösung: Factory Method
Das Factory Method Pattern löst diese Probleme, indem es die Objekterstellung in eine separate Methode auslagert. Der Client muss nicht mehr wissen, wie ein Objekt genau erzeugt wird – er ruft nur die passende Fabrikmethode auf.

Grundidee:
- Statt Objekte direkt mit
newzu erstellen, rufst du eine Factory-Methode auf. - Die Factory entscheidet, welches konkrete Objekt erzeugt wird.
Die Einfache Fabrik (Simple Factory)
Bevor wir das klassische Fabrikmethoden-Muster betrachten, beginnen wir mit einer sehr verbreiteten und einfacheren Variante: der Einfachen Fabrik (oft auch als Static Factory bezeichnet).
Ihr Hauptmerkmal ist eine zentrale Klasse mit einer einzigen Methode, die für die Erzeugung verschiedener Objekte zuständig ist. Diese Methode erhält typischerweise einen Parameter (z.B. einen String oder eine Enum), der bestimmt, welche konkrete Klasse instanziiert werden soll. Obwohl sie nicht streng dem klassischen “Factory Method”-Pattern entspricht, löst sie das Kernproblem: Die Entkopplung des Clients von der direkten Erstellung konkreter Objekte.
Das folgende Beispiel mit den Fahrzeugen demonstriert dieses Prinzip perfekt.
Praktisches Beispiel: Fahrzeug-Factory
Wir betrachten ein Beispiel aus der Praxis, bei dem verschiedene Fahrzeugtypen erzeugt werden.
1. Das Produkt-Interface
public interface Fahrzeug {
void starten(); // Startet das Fahrzeug
void fahren(); // Fahrzeug führt eine Fahrt aus
String getTyp(); // Gibt den Typ des Fahrzeugs zurück
}Erklärung des Codes
Dieser Java-Code definiert ein interface (eine Schnittstelle) namens Fahrzeug.
- Was ist ein Interface? Ein
interfaceist ein reiner “Bauplan” oder “Vertrag”. Es legt fest, welche Methoden (Funktionen) eine Klasse haben muss, wenn sie diesen Vertrag “unterschreibt” (implementiert). Es sagt aber nicht, wie diese Methoden funktionieren. Fahrzeug: Dies ist der Name des Vertrags.- Die Methoden (
starten,fahren,getTyp):- Dies sind abstrakte Methoden. Sie haben keinen Code-Körper (keine
{...}). void starten(): Jede Klasse, dieFahrzeugist, muss einestarten-Funktion haben.void fahren(): Sie muss auch einefahren-Funktion haben.String getTyp(): Sie muss einegetTyp-Funktion haben, die einen Text (String) zurückgibt.
- Dies sind abstrakte Methoden. Sie haben keinen Code-Körper (keine
Zweck: Eine Klasse wie Auto oder Motorrad kann nun implements Fahrzeug schreiben. Java zwingt diese Klasse dann dazu, alle drei Methoden (starten, fahren, getTyp) konkret auszuprogrammieren.
Praktisches Beispiel: Fahrzeug-Factory
2. Konkrete Produkte
public class Auto implements Fahrzeug {
@Override
public void starten() {
System.out.println("Auto: Motor startet!");
}
@Override
public void fahren() {
System.out.println("Auto fährt auf der Straße");
}
@Override
public String getTyp() {
return "Auto";
}
}
public class Motorrad implements Fahrzeug {
@Override
public void starten() {
System.out.println("Motorrad: Motor brummt!");
}
@Override
public void fahren() {
System.out.println("Motorrad fährt schnell");
}
@Override
public String getTyp() {
return "Motorrad";
}
}Erklärung des Codes
Dieser Code zeigt zwei konkrete Klassen (Auto und Motorrad), die den “Vertrag” (das interface) Fahrzeug aus dem vorigen Beispiel implementieren.
implements Fahrzeug: Diese Anweisung bedeutet, dass die Klasse (z.B.Auto) “unterschreibt” und verspricht, alle imFahrzeug-Interface definierten Methoden bereitzustellen.@Override: Diese Markierung (Annotation) zeigt an, dass die folgende Methode (starten,fahren,getTyp) eine Methode aus dem InterfaceFahrzeugüberschreibt bzw. konkret implementiert.- Spezifisches Verhalten: Obwohl beide Klassen dieselben Methoden (
starten,fahren) haben müssen, ist ihre Implementierung (der Code-Inhalt) unterschiedlich. DasAutostartet anders als dasMotorrad. Das ist der Kernpunkt von Interfaces: Sie definieren ein einheitliches “Was” (einestarten-Methode), aber jede Klasse entscheidet selbst über das “Wie”.
Praktisches Beispiel: Fahrzeug-Factory
3. Die Factory-Klasse
public class FahrzeugFactory {
// Factory-Methode zur Erstellung von Fahrzeugen
public static Fahrzeug erstelleFahrzeug(String typ) {
switch (typ.toLowerCase()) {
case "auto":
return new Auto(); // Erstellt ein Auto
case "motorrad":
return new Motorrad(); // Erstellt ein Motorrad
default:
throw new IllegalArgumentException("Unbekannter Fahrzeugtyp: " + typ);
}
}
}Erklärung des Codes
Dieser Code implementiert das (Simple) Factory-Muster. Es löst das Problem aus dem SpielClient-Beispiel (die if/elif-Kette zur Objekterzeugung), indem es die Erstellung von Objekten zentralisiert.
FahrzeugFactory: Dies ist eine Klasse, die als “Fabrik” dient. Ihre einzige Aufgabe ist es,Fahrzeug-Objekte zu erstellen.public static Fahrzeug erstelleFahrzeug(String typ): Dies ist die eigentliche Fabrikmethode.static: Die Methode gehört zur Klasse selbst, nicht zu einem Objekt. Man kann sie direkt aufrufen (FahrzeugFactory.erstelleFahrzeug(...)), ohnenew FahrzeugFactory()erstellen zu müssen.Fahrzeug: Der Rückgabetyp ist dasFahrzeug-Interface. Das bedeutet, die Methode gibt irgendein Objekt zurück, das denFahrzeug-Vertrag erfüllt.String typ: Dies ist die Eingabe, z.B. “auto” oder “motorrad”.
switch (typ.toLowerCase()): Die Methode nutzt eineswitch-Anweisung (eine saubere Alternative zurif/elif-Kette), um dentypzu prüfen.toLowerCase()stellt sicher, dass “Auto” und “auto” gleich behandelt werden.case "auto": return new Auto();: Wenn der Typ “auto” ist, erstellt die Fabrik einnew Auto()und gibt es zurück.- Vorteil: Der Rest des Programms muss die konkreten Klassen (
Auto,Motorrad) nicht mehr kennen. Wenn ein neuer Fahrzeugtyp (z.B.LKW) hinzukommt, muss nur diese Factory-Klasse angepasst werden.
Praktisches Beispiel: Fahrzeug-Factory
4. Verwendung
public class FahrzeugApp {
public static void main(String[] args) {
// Erstellt ein Auto über die Factory
Fahrzeug meinFahrzeug = FahrzeugFactory.erstelleFahrzeug("auto");
meinFahrzeug.starten();
meinFahrzeug.fahren();
// Erstellt ein Motorrad über die Factory
Fahrzeug zweitesFahrzeug = FahrzeugFactory.erstelleFahrzeug("motorrad");
zweitesFahrzeug.starten();
zweitesFahrzeug.fahren();
}
}Erklärung des Codes
Dieser Code (FahrzeugApp) zeigt, wie die FahrzeugFactory (aus dem vorigen Beispiel) von einem “Client” (dem Hauptprogramm) genutzt wird.
- Entkoppelte Erstellung: Das Hauptprogramm (
main) ruftFahrzeugFactory.erstelleFahrzeug("auto")auf. Es sagt der Fabrik was es will (ein “auto”), aber es kümmert sich nicht darum, wie es erstellt wird (es ruft nichtnew Auto()auf). - Nutzung über Interface: Das zurückgegebene Objekt (ein
Auto-Objekt) wird in einer Variable vom TypFahrzeug(dem Interface/Vertrag) gespeichert. - Polymorphismus: Das Programm ruft
meinFahrzeug.starten()auf. ObwohlmeinFahrzeugnur alsFahrzeugbekannt ist, führt Java die spezifischestarten()-Methode derAuto-Klasse aus. - Flexibilität: Dasselbe passiert beim Erstellen des Motorrads. Das Hauptprogramm verwendet exakt denselben Aufrufstil, bekommt aber ein anderes Objekt mit anderem Verhalten (
Motorrad-Implementierung).
Der FahrzeugApp-Code muss die konkreten Klassen Auto oder Motorrad nie kennen oder importieren. Er interagiert nur mit der FahrzeugFactory und dem Fahrzeug-Interface.
Vorteile des Fabrikmethoden-Musters
Das Fabrikmethoden-Muster bietet mehrere wesentliche Vorteile in der Softwareentwicklung, die vor allem auf die Prinzipien der Entkopplung und Flexibilität abzielen.
-
Starke Entkopplung von Code Der größte Vorteil ist die Entkopplung. Der Client-Code (in unserem Beispiel die
Anwendung-Klasse) arbeitet nur gegen das abstrakteProdukt-Interface (Dokument). Er muss die konkreten Implementierungen (WordDokument,PdfDokument) nicht kennen. Dadurch können die konkreten Produktklassen geändert oder ausgetauscht werden, ohne den Client-Code anpassen zu müssen. -
Einfache Erweiterbarkeit (Open/Closed-Prinzip) Das Muster folgt dem Open/Closed-Prinzip: Die Software ist offen für Erweiterungen, aber geschlossen für Modifikationen. Um einen neuen Produkttyp hinzuzufügen (z.B. ein
ExcelDokument), müssen Sie lediglich eine neue Produktklasse und eine neue Erzeuger-Klasse (ExcelAnwendung) erstellen. Bestehender Code muss nicht verändert werden, was das Risiko von Fehlern in der bestehenden Funktionalität minimiert.
Vorteile des Fabrikmethoden-Musters
-
Zentralisierung der Erstellungslogik Die Logik zur Erstellung eines Objekts ist an einem einzigen, klar definierten Ort gekapselt: in der Fabrikmethode der jeweiligen Erzeuger-Klasse. Wenn sich der Konstruktor eines Produkts ändert oder die Initialisierung komplexer wird, muss die Anpassung nur an dieser einen Stelle erfolgen. Dies verbessert die Wartbarkeit und verhindert doppelten Code.
-
Kontrolle durch Unterklassen Das klassische Muster gibt den Unterklassen die volle Kontrolle darüber, welche Objekte erzeugt werden. Die abstrakte Oberklasse (
Anwendung) legt den Rahmen und die allgemeine Logik fest (das “Wann” und “Wie” der Nutzung), aber die konkreten Unterklassen (WordAnwendung) entscheiden über das spezifische Objekt (das “Was”). Dies ist besonders in Frameworks nützlich, wo das Framework die Struktur vorgibt, die konkrete Implementierung aber dem Nutzer überlassen wird. -
Verbesserte Lesbarkeit und Wartbarkeit Indem die Erstellungslogik aus der allgemeinen Geschäftslogik entfernt wird, wird der Code sauberer und leichter verständlich. Statt
new-Anweisungen im gesamten Code verteilt zu haben, gibt es klar benannte Methoden, deren einziger Zweck die Objekterzeugung ist.
Wann solltest du das Factory Method Pattern verwenden?
Damit du die Einsatzgebiete besser einschätzen kannst, zeigt die folgende Tabelle, wann das Pattern geeignet ist und wann nicht.
| Situation | Empfehlung |
|---|---|
| Du musst verschiedene Varianten eines Objekttyps erzeugen | Verwenden |
| Die Objekterstellung ist komplex oder benötigt viele Parameter | Verwenden |
| Du möchtest neue Typen hinzufügen, ohne bestehenden Code zu ändern | Verwenden |
| Die Auswahl des Objekttyps erfolgt zur Laufzeit | Verwenden |
| Du möchtest für Tests Mock-Objekte einsetzen | Verwenden |
| Es gibt nur einen einzigen Objekttyp | Nicht verwenden |
| Die Objekterstellung ist sehr einfach | Nicht verwenden |
| Du möchtest unnötige Abstraktion und Overengineering vermeiden | Nicht verwenden |
Das klassische Fabrikmethoden-Muster (Factory Method)
Nachdem wir die “Einfache Fabrik” kennengelernt haben, folgt nun das klassische Fabrikmethoden-Entwurfsmuster, wie es ursprünglich von der “Gang of Four” definiert wurde.
Der entscheidende Unterschied liegt im Einsatz von Vererbung. Anstatt einer einzigen Klasse, die über einen Parameter entscheidet, definieren wir hier eine Hierarchie von Erzeuger-Klassen.
Die Struktur sieht wie folgt aus:
- Eine abstrakte Erzeuger-Klasse (Creator) deklariert die abstrakte Fabrikmethode.
- Konkrete Unterklassen (Concrete Creators) implementieren diese Methode und legen so fest, welches spezifische Produkt sie erzeugen.
Dieses Vorgehen überlässt die Verantwortung für die Objekterstellung vollständig den Unterklassen und bietet dadurch ein höheres Maß an Flexibilität, insbesondere in größeren Anwendungs-Frameworks. Das nachfolgende Beispiel einer Dokumenten-Anwendung verdeutlicht diesen Aufbau.
Praxisbeispiel: Fabrikmethode
1. Das Produkt-Interface
// Definiert die gemeinsamen Operationen für alle Dokumenttypen
public interface Dokument {
void oeffnen();
void speichern();
void schliessen();
}Erklärung des Codes
Dieser Java-Code definiert ein interface (eine Schnittstelle) namens Dokument.
Ein interface ist ein reiner Vertrag. Es legt fest, welche Methoden (Funktionen) eine Klasse bereitstellen muss, wenn sie diesen Vertrag “unterschreibt” (implementiert).
In diesem Fall legt der Vertrag Dokument fest, dass jede Klasse, die ein Dokument sein will (z.B. TextDokument oder PDFDokument), die folgenden drei Methoden implementieren muss:
void oeffnen()void speichern()void schliessen()
Praxisbeispiel: Fabrikmethode
2. Konkrete Produkte
// Konkrete Implementierung für ein Word-Dokument
public class WordDokument implements Dokument {
@Override
public void oeffnen() {
System.out.println("Word-Dokument wird geöffnet...");
}
@Override
public void speichern() {
System.out.println("Word-Dokument wird als .docx gespeichert.");
}
@Override
public void schliessen() {
System.out.println("Word-Dokument wird geschlossen.");
}
}
// Konkrete Implementierung für ein PDF-Dokument
public class PdfDokument implements Dokument {
@Override
public void oeffnen() {
System.out.println("PDF-Dokument wird im Reader geöffnet...");
}
@Override
public void speichern() {
System.out.println("PDF-Dokument wird als .pdf gespeichert.");
}
@Override
public void schliessen() {
System.out.println("PDF-Dokument wird geschlossen.");
}
}Erklärung des Codes
Dieser Code zeigt zwei konkrete Implementierungen des Dokument-Vertrags (Interfaces) aus dem vorigen Beispiel.
implements Dokument: Beide Klassen (WordDokumentundPdfDokument) “unterschreiben” denDokument-Vertrag. Das bedeutet, Java zwingt sie, alle im Interface definierten Methoden (oeffnen,speichern,schliessen) bereitzustellen.@Override: Diese Markierung (Annotation) signalisiert: “Die folgende Methode ist unsere spezifische Implementierung einer Methode, die vom Interface (oder einer Mutterklasse) verlangt wird.”- Spezifisches Verhalten: Obwohl beide Klassen dieselben Methoden haben, ist der Code innerhalb der Methoden unterschiedlich.
WordDokument.speichern()tut etwas anderes alsPdfDokument.speichern(). Das ist der Kernzweck von Interfaces: Sie erzwingen ein einheitliches “Was” (alle müssen speichern können), erlauben aber ein unterschiedliches “Wie” (Word speichert als.docx, PDF als.pdf).
Praxisbeispiel: Fabrikmethode
3. Der abstrakte Erzeuger (Creator)
// Die abstrakte Creator-Klasse
public abstract class Anwendung {
// Die eigentliche Fabrikmethode. Sie ist abstrakt.
// Unterklassen MÜSSEN diese Methode implementieren, um das spezifische Produkt zu erstellen.
protected abstract Dokument erstelleDokument();
// Diese Methode nutzt die Fabrikmethode, um ein Dokument zu erstellen
// und damit zu arbeiten. Die Logik hier ist für alle Dokumenttypen gleich.
public void neuesDokument() {
System.out.println("Anwendung: Starte Logik zur Dokumentenerstellung.");
Dokument doc = erstelleDokument(); // Hier wird die Fabrikmethode aufgerufen
doc.oeffnen();
doc.speichern();
}
}Erklärung des Codes
Dieser Code zeigt das Factory Method Pattern (Fabrikmethode-Muster). Der Kern des Musters besteht darin, die Entscheidung, welches Objekt erstellt wird, an Unterklassen (Kindklassen) zu delegieren.
public abstract class Anwendung: Dies ist eine abstrakte Klasse – eine unvollständige “Vorlage”. Man kann keinAnwendung-Objekt direkt erstellen. Sie dient als Basis für spezialisierte Anwendungen (z.B. eineWordAnwendung).protected abstract Dokument erstelleDokument():- Dies ist die “Factory-Methode”.
abstract: Die Methode hat keinen Code. Sie ist ein Zwang: Jede konkrete Kindklasse (z.B.WordAnwendung) muss diese Methode implementieren und definieren, welchesDokumentsie erstellt.
public void neuesDokument():- Dies ist eine konkrete Methode, die einen festen Arbeitsablauf definiert.
- Sie ruft
erstelleDokument()auf. Sie weiß nicht, welche Implementierung dieser Methode ausgeführt wird – nur, dass sie einDokument-Objekt zurückbekommt. - Der Clou: Wenn eine
WordAnwendungdiese Methode aufruft, wirdWordAnwendung.erstelleDokument()ausgeführt. Wenn einePdfAnwendungsie aufruft, wirdPdfAnwendung.erstelleDokument()ausgeführt. - Der Rest der Logik (
doc.oeffnen(),doc.speichern()) ist generisch und funktioniert mit jedemDokument.
Praxisbeispiel: Fabrikmethode
4. Konkrete Erzeuger (Concrete Creators)
// Ein konkreter Erzeuger für Word-Dokumente
public class WordAnwendung extends Anwendung {
@Override
protected Dokument erstelleDokument() {
// Diese Fabrik ist dafür verantwortlich, Word-Dokumente zu erstellen.
return new WordDokument();
}
}
// Ein konkreter Erzeuger für PDF-Dokumente
public class PdfAnwendung extends Anwendung {
@Override
protected Dokument erstelleDokument() {
// Diese Fabrik ist dafür verantwortlich, PDF-Dokumente zu erstellen.
return new PdfDokument();
}
}Erklärung des Codes
Dieser Code zeigt die konkreten Kindklassen, die die abstrakte Anwendung-Vorlage (aus dem vorigen Beispiel) mit Leben füllen.
extends Anwendung: SowohlWordAnwendungals auchPdfAnwendungerben von derAnwendung-Klasse.@Override protected Dokument erstelleDokument(): Weil dieAnwendung-Klasse die MethodeerstelleDokument()alsabstract(Pflicht zur Implementierung) deklariert hat, müssen diese Kindklassen sie implementieren (anzezeigt durch@Override).- Die Entscheidung (Der Kern des Musters): Hier wird nun die “Fabrikmethode” konkretisiert:
- Die
WordAnwendungentscheidet sich, einnew WordDokument()zurückzugeben. - Die
PdfAnwendungentscheidet sich, einnew PdfDokument()zurückzugeben.
- Die
Der allgemeine Algorithmus (neuesDokument() in der Anwendung-Klasse) bleibt derselbe, aber die spezifische Klasse, die erstellt wird, wird von der jeweiligen Kindklasse (WordAnwendung oder PdfAnwendung) bestimmt.
Praxisbeispiel: Fabrikmethode
5. Verwendung (Client)
public class Client {
public static void main(String[] args) {
Anwendung meineAnwendung;
String konfiguration = "word"; // Diese Einstellung könnte z.B. aus einer Datei kommen
// Abhängig von der Konfiguration wird die passende Fabrik gewählt
if (konfiguration.equalsIgnoreCase("word")) {
meineAnwendung = new WordAnwendung();
} else {
meineAnwendung = new PdfAnwendung();
}
// Der Client-Code arbeitet nun mit der abstrakten Anwendung,
// ohne die konkreten Dokumentenklassen zu kennen.
meineAnwendung.neuesDokument();
System.out.println("\n--- Konfiguration geändert ---");
// Wechsel zur PDF-Anwendung
meineAnwendung = new PdfAnwendung();
meineAnwendung.neuesDokument();
}
}Erklärung des Codes
Dieser Client-Code (das Hauptprogramm) demonstriert, wie die Fabrikmethode (Factory Method) aus den vorigen Beispielen genutzt wird, um das Programm zur Laufzeit flexibel zu konfigurieren.
- Konfiguration: Der Code simuliert eine externe Einstellung (z.B. aus einer Konfigurationsdatei) mit der Variable
konfiguration = "word". - Auswahl der Fabrik: Der
if-Block prüft diese Konfiguration. Er entscheidet, welche konkrete Fabrik (WordAnwendungoderPdfAnwendung) erstellt wird. - Abstrakte Variable: Das erstellte Objekt (z.B.
new WordAnwendung()) wird in der VariablemeineAnwendunggespeichert. Wichtig ist, dass diese Variable den abstrakten TypAnwendunghat. - Entkoppelte Nutzung: Der Client ruft
meineAnwendung.neuesDokument()auf. Dieser Aufruf ist generisch. Der Client-Code weiß nicht (und es ist ihm egal), obmeineAnwendungeineWordAnwendungoderPdfAnwendungist. Er weiß nur, dass es eineAnwendungist, die die MethodeneuesDokument()hat. - Das Ergebnis (Polymorphismus): Intern ruft
neuesDokument()die (überschriebene)erstelleDokument()-Methode auf. DameineAnwendung(im ersten Fall) einWordAnwendung-Objekt ist, wird dessenerstelleDokument()-Methode ausgeführt, und einWordDokumentwird erstellt. Im zweiten Fall (PdfAnwendung) wird einPdfDokumenterstellt.
Herausforderungen
Mögliche Herausforderungen sind:
- Komplexität: Mehr Klassen und zusätzlicher Code.
- Overhead: Zusätzliche Abstraktionsebene.
- Lernkurve: Einsteiger müssen das Pattern erst verstehen.
Zusammenfassung
Zusammenfassung:
Factory Method Pattern – Grundlagen
Das Factory Method Pattern gehört zur Kategorie der Erzeugungsmuster in der Softwareentwicklung. Es löst typische Probleme bei der Objekterstellung, indem es die Instanziierung in eine separate Fabrikmethode auslagert.
- Der Client-Code muss nicht mehr wissen, wie Objekte erstellt werden.
- Die Objekterstellung wird von der Nutzung entkoppelt.
- Neue Typen lassen sich einfach hinzufügen, ohne den Client-Code anzupassen.
- Der Code wird übersichtlicher, wartbarer und besser testbar.
- Durch Factory-Methoden können aussagekräftige Namen verwendet werden, die den Code verständlicher machen.
Funktionsweise des Factory Method Patterns
Die Grundidee ist, Objekte nicht direkt mit new zu erstellen, sondern eine Factory-Methode aufzurufen, die das passende Objekt zurückgibt.
- Produkt-Interface: Definiert die gemeinsamen Methoden, die alle Produkte implementieren.
- Konkrete Produkte: Implementieren das Interface mit spezifischem Verhalten.
- Factory-Klasse: Enthält die Fabrikmethode, die je nach Eingabe das passende Objekt erzeugt.
- Verwendung im Client: Der Client-Code ruft nur noch die Factory auf und kennt die konkreten Klassen nicht.
Beispiel: Eine Fahrzeug-Factory erstellt verschiedene Fahrzeugtypen wie Auto, Motorrad oder Fahrrad. Der Client arbeitet dabei ausschließlich mit dem Interface Fahrzeug.
Nutzen und Einsatz des Factory Method Patterns
Das Pattern bietet klare Vorteile, hat aber auch Grenzen.
Vorteile:
- Entkopplung des Client-Codes von konkreten Klassen.
- Einfache Erweiterbarkeit durch Hinzufügen neuer Typen.
- Verbesserte Wartbarkeit, da Änderungen zentral in der Factory erfolgen.
- Verbesserte Testbarkeit durch einfache Erzeugung von Mock-Objekten.
- Gesteigerte Lesbarkeit durch sprechende Methodennamen.
Nachteile:
- Zusätzliche Komplexität durch mehr Klassen und Abstraktionsebenen.
- Overhead, wenn die Objekterstellung sehr einfach wäre.
- Lernkurve für Einsteiger, die das Pattern erst verstehen müssen.
Einsatzgebiete:
- Sinnvoll bei komplexer Objekterstellung oder wenn mehrere Varianten eines Typs benötigt werden.
- Geeignet, wenn neue Typen flexibel ergänzt werden sollen.
- Praktisch für Tests, da Mock-Objekte einfach über die Factory eingebunden werden können.
- Nicht geeignet bei sehr einfachen Szenarien oder wenn es nur einen einzigen Objekttyp gibt.
Ausblick:
In der nächsten Lerneinheit lernst du das Beobachtermuster (Observer Pattern) kennen – ein Entwurfsmuster, das es ermöglicht, dass Objekte automatisch über Änderungen anderer Objekte informiert werden. Du wirst verstehen, wie dieses Prinzip für reaktive und ereignisgesteuerte Systeme genutzt wird und warum es in modernen Anwendungen, etwa bei Benutzeroberflächen oder Event-Systemen, eine zentrale Rolle spielt.