Erzeugungsmuster
Factory Method
Ein Client braucht ein Objekt, dessen konkreter Typ von Daten abhängt — vom Dateiformat, vom Zahlungsanbieter, vom Nachrichtentyp. Ohne Muster steht diese Fallunterscheidung dort, wo das Objekt gebraucht wird, und wird bei jeder Erweiterung an mehreren Stellen nachgezogen. Die Fabrik zieht die Entscheidung an genau einen Ort.
Struktur
Alle Varianten implementieren eine gemeinsame Schnittstelle. Eine Fabrikmethode nimmt den Unterscheidungsparameter entgegen und liefert die passende Implementierung zurück.
interface Versand
{
public function liefern(Sendung $sendung): Beleg;
}
final class VersandFactory
{
public function fuer(Sendung $sendung): Versand
{
return match (true) {
$sendung->gewichtKg > 30 => new Spedition(),
$sendung->istAusland() => new Paketdienst(),
default => new Eigenlieferung(),
};
}
}
Die Abgrenzung zur reinen Terminologie: Das GoF-Buch meint mit Factory Method die Fabrikmethode in einer Oberklasse, die Unterklassen überschreiben, und mit Abstract Factory eine Fabrik für ganze Produktfamilien. In der Praxis heißt alles davon „Factory". Wichtiger als die Zuordnung ist, ob die Entscheidung tatsächlich an einer Stelle steht.
Praxisnotiz
Die Fabrik verschiebt eine Fallunterscheidung, sie beseitigt sie nicht. Das ist der Gewinn und zugleich die Falle. Der Gewinn: Eine neue Variante ist eine neue Klasse plus eine Zeile in der Fabrik, der aufrufende Code bleibt unberührt. Die Falle: Wenn die Fabrik Parameter durchreicht, die nur eine Variante braucht, wandert die Komplexität in die Signatur statt zu verschwinden — dann steht die Verzweigung eben zweimal da.
Woran ich in Reviews merke, dass eine Fabrik trägt: Der Client kennt den
konkreten Typ nirgends mehr. Sobald hinter dem Fabrikaufruf noch ein
instanceof folgt, ist die Abstraktion nicht dicht, und der Aufwand hat sich
nicht gelohnt.
Der zweite häufige Fund sind Fabriken, die selbst zur Gottklasse werden: eine
zentrale ObjectFactory, die alles erzeugt, was im System vorkommt. Dann ist
die Kopplung nicht weg, sondern nur an einem Ort konzentriert — und dieser Ort
ändert sich bei jedem Feature.
Wann es nicht passt
-
Bei genau zwei Varianten, die sich absehbar nicht vermehren. Ein
ifist dann ehrlicher und kürzer als eine Klasse mehr. -
Wenn die Auswahl ohnehin ein DI-Container übernimmt: Dort ist die Zuordnung von Schnittstelle zu Implementierung Konfiguration.
-
Wenn nach der Erzeugung noch viele abhängige Parameter zu setzen sind — das ist ein Fall für den Builder, nicht für eine Fabrik.