Factory-Muster
Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy — Lektion 72 von 91.
Das Factory Pattern ist ein Erzeugungsmuster, das die Objekterzeugung an eine separate Klasse oder Methode delegiert. Anstatt new direkt in Ihrem gesamten Code zu verwenden, bitten Sie eine Factory, Objekte für Sie zu erstellen.
Dieses Muster ist besonders nützlich, wenn Sie mehrere verwandte Klassen haben, die eine gemeinsame Schnittstelle teilen, und Sie entscheiden möchten, welche davon basierend auf einer bestimmten Bedingung instanziiert werden soll. Stellen Sie sich ein Benachrichtigungssystem vor, das Nachrichten über verschiedene Kanäle senden kann:
<?php
interface Notification {
public function send(string $message): string;
}
class EmailNotification implements Notification {
public function send(string $message): string {
return "Email: $message";
}
}
class SmsNotification implements Notification {
public function send(string $message): string {
return "SMS: $message";
}
}
Ohne eine Factory würden Sie if-Anweisungen und new-Aufrufe überall verteilen. Die Factory zentralisiert diese Logik:
<?php
class NotificationFactory {
public static function create(string $type): Notification {
return match($type) {
'email' => new EmailNotification(),
'sms' => new SmsNotification(),
default => throw new InvalidArgumentException("Unknown type: $type")
};
}
}
$notification = NotificationFactory::create('email');
echo $notification->send('Hello!');
Ausgabe:
Email: Hello!Der entscheidende Vorteil ist, dass Ihr Code von dem Notification Interface abhängt und nicht von konkreten Klassen. Wenn Sie später eine PushNotification Klasse hinzufügen, aktualisieren Sie nur die Factory. Der gesamte Code, der die Factory verwendet, erhält automatisch Zugriff auf den neuen Typ, ohne dass Änderungen erforderlich sind.
Aufgabe
EinfachLassen Sie uns ein Dokumentengenerierungssystem mit dem Factory Pattern erstellen. Verschiedene Dokumenttypen (PDF, HTML, Plain Text) teilen sich ein gemeinsames Interface, erzeugen aber unterschiedliche Ausgaben — ein perfektes Szenario, um die Objekterstellung in einer Factory zu zentralisieren.
Sie werden Ihren Code über vier Dateien organisieren:
Document.php— Erstellen Sie einDocumentInterface, das den Vertrag definiert, dem alle Dokumenttypen folgen müssen. Es sollte eine einzige Methoderender(string $content): stringhaben, die den Inhalt entgegennimmt und die formatierte Dokumentenausgabe zurückgibt.Documents.php— Binden Sie das Document-Interface ein und erstellen Sie drei Klassen, die es implementieren:PdfDocument— Seinerender()Methode gibt"PDF: $content"zurück.HtmlDocument— Seinerender()Methode gibt"<html>$content</html>"zurück.TextDocument— Seinerender()Methode gibt"TXT: $content"zurück.
DocumentFactory.php— Binden Sie die Datei Documents ein und erstellen Sie eineDocumentFactoryKlasse mit einer statischen Methodecreate(string $type): Document. Diese Methode solltematchverwenden, um basierend auf dem Typ das entsprechende Dokumentobjekt zurückzugeben:"pdf"gibt ein neuesPdfDocumentzurück."html"gibt ein neuesHtmlDocumentzurück."text"gibt ein neuesTextDocumentzurück.- Jeder andere Typ sollte eine
InvalidArgumentExceptionmit der Nachricht"Unknown document type: $type"werfen.
main.php— Binden Sie die Datei DocumentFactory ein. Sie erhalten zwei Eingaben: einen Dokumenttyp und den zu rendernden Inhalt.Verwenden Sie die Factory, um den entsprechenden Dokumenttyp zu erstellen, rufen Sie dann dessen
render()Methode mit dem Inhalt auf und geben Sie das Ergebnis aus.Geben Sie in einer neuen Zeile
"Factory benefit: Adding new types only requires updating the factory"aus, um hervorzuheben, warum dieses Pattern wertvoll ist.
Das Factory Pattern hält Ihren Code flexibel — wenn Sie später ein MarkdownDocument hinzufügen müssen, aktualisieren Sie nur die Factory, während der gesamte bestehende Code unverändert weiterfunktioniert.
Spickzettel
Das Factory Pattern delegiert die Objekterstellung an eine separate Klasse oder Methode, anstatt new direkt im gesamten Code zu verwenden.
Dieses Muster ist nützlich, wenn Sie mehrere verwandte Klassen haben, die eine gemeinsame Schnittstelle teilen, und Sie basierend auf einer Bedingung entscheiden müssen, welche davon instanziiert werden soll.
Beispiel mit einem Benachrichtigungssystem:
<?php
interface Notification {
public function send(string $message): string;
}
class EmailNotification implements Notification {
public function send(string $message): string {
return "Email: $message";
}
}
class SmsNotification implements Notification {
public function send(string $message): string {
return "SMS: $message";
}
}
Die Factory zentralisiert die Logik zur Objekterstellung:
<?php
class NotificationFactory {
public static function create(string $type): Notification {
return match($type) {
'email' => new EmailNotification(),
'sms' => new SmsNotification(),
default => throw new InvalidArgumentException("Unknown type: $type")
};
}
}
$notification = NotificationFactory::create('email');
echo $notification->send('Hello!'); // Ausgabe: Email: Hello!
Hauptvorteile:
- Der Code hängt vom Interface ab, nicht von konkreten Klassen
- Das Hinzufügen neuer Typen erfordert nur die Aktualisierung der Factory
- Bestehender Code erhält automatisch Zugriff auf neue Typen, ohne dass Änderungen erforderlich sind
Probier es selbst
<?php
require_once 'DocumentFactory.php';
// Eingabe lesen
$type = trim(fgets(STDIN));
$content = trim(fgets(STDIN));
// TODO: DocumentFactory verwenden, um den entsprechenden Dokumenttyp zu erstellen
// TODO: Die render()-Methode mit dem Inhalt aufrufen und das Ergebnis ausgeben
// TODO: In einer neuen Zeile "Factory benefit: Adding new types only requires updating the factory" ausgeben
?>Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas $this-SchlüsselwortMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick - Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDas parent:: SchlüsselwortMethoden überschreibenDas final SchlüsselwortAbstrakte KlassenZusammenfassung – Mitarbeiter-Hierarchie7Kapselung
Public, Protected, PrivateZugriffsmodifikatoren im DetailGetter und SetterInformation HidingConstructor Promotion (8.0)Zusammenfassung – Studentenverwaltungssystem2Namensräume & Autoloading
Einführung in NamensräumeDas use-SchlüsselwortPSR-4 Autoloading-StandardComposer AutoloaderZusammenfassung – Organisiertes Projekt5Interfaces & Verträge
Einführung in InterfacesInterfaces implementierenMehrfache Interface-ImplementierungInterface vs. Abstrakte KlasseType Hinting mit InterfacesZusammenfassung – Formen-Rechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objekt-Klonen__serialize & __unserializeZusammenfassung - Eigene Collection11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenEigene Exception-HierarchieTry, Catch, FinallyZusammenfassung - Formular-Validator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klasseneigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenReadonly-Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphismus
Method Overriding erneut betrachtetPolymorphismus über InterfacesType Hinting & Union TypesLate Static BindingZusammenfassung – Payment Processor9Traits
Einführung in TraitsVerwendung mehrerer TraitsKonfliktlösung bei TraitsAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung