Factory-Muster
Teil des Abschnitts Objektorientierte Programmierung der Dart-Journey von Coddy — Lektion 91 von 110.
Das Factory-Pattern delegiert die Objekterstellung an eine separate Methode oder Klasse, sodass Sie Objekte erstellen können, ohne deren genaue Klasse anzugeben. Dies unterscheidet sich vom Factory-Konstruktor, den Sie zuvor kennengelernt haben – das Factory-Pattern ist ein Designansatz, bei dem die Erstellungslogik zentralisiert ist und basierend auf der Eingabe verschiedene Typen zurückgeben kann.
Stellen Sie sich ein Szenario vor, in dem Sie verschiedene Arten von Benachrichtigungen erstellen müssen. Anstatt dass der Client entscheidet, welche Klasse instanziiert werden soll, übernimmt eine Factory diese Entscheidung:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}Der Client-Code muss nichts über EmailNotification oder SMSNotification wissen – er fragt einfach die Factory nach einer Benachrichtigung eines bestimmten Typs. Dies macht das Hinzufügen neuer Benachrichtigungstypen einfach: Sie ändern nur die Factory, nicht jede Stelle, an der Benachrichtigungen erstellt werden.
Verwenden Sie das Factory-Pattern, wenn die Objekterstellung komplexe Logik beinhaltet, wenn Sie Implementierungsklassen vor Clients verbergen möchten oder wenn der genaue zu erstellende Typ von Laufzeitbedingungen abhängt.
Aufgabe
EinfachLassen Sie uns einen Dokumentengenerator mit dem Factory-Pattern erstellen! Sie werden ein System entwickeln, das verschiedene Arten von Dokumenten (Berichte, Rechnungen und Briefe) über eine zentrale Factory erzeugt, sodass der Client-Code die spezifischen Dokumentklassen nicht kennen muss.
Sie werden Ihren Code in zwei Dateien organisieren:
document_factory.dart: Erstellen Sie hier Ihr Dokumentensystem:- Eine abstrakte Klasse
Documentmit einer Methodegenerate(String content), die einenStringzurückgibt - Drei konkrete Klassen, die
Documentimplementieren:Report- seinegenerate-Methode gibt=== REPORT ===\n[content]\n=============zurückInvoice- seinegenerate-Methode gibtINVOICE\n--------\n[content]\nTotal DuezurückLetter- seinegenerate-Methode gibtDear Reader,\n[content]\nSincerelyzurück
- Eine
DocumentFactory-Klasse mit einer statischen Methodecreate(String type), die das entsprechendeDocumentbasierend auf dem Typ-String zurückgibt:"report","invoice"oder"letter". Geben Sie für jeden unbekannten Typ standardmäßig einLetter-Objekt zurück
- Eine abstrakte Klasse
main.dart: Importieren Sie Ihre Dokument-Factory und demonstrieren Sie, wie die Factory verschiedene Dokumenttypen erstellt, ohne dass der Client die konkreten Klassen kennen muss:- Verwenden Sie die Factory, um einen Bericht zu erstellen, und geben Sie das Ergebnis der Generierung mit dem Inhalt
Sales increased by 25%aus - Geben Sie eine Leerzeile aus
- Verwenden Sie die Factory, um eine Rechnung zu erstellen, und geben Sie das Ergebnis der Generierung mit dem Inhalt
Widget x3 - $150aus - Geben Sie eine Leerzeile aus
- Verwenden Sie die Factory, um einen Brief zu erstellen, und geben Sie das Ergebnis der Generierung mit dem Inhalt
Thank you for your orderaus
- Verwenden Sie die Factory, um einen Bericht zu erstellen, und geben Sie das Ergebnis der Generierung mit dem Inhalt
Beachten Sie, dass Ihre Hauptdatei nur mit der Factory und dem abstrakten Typ Document interagiert – sie erstellt niemals direkt Report-, Invoice- oder Letter-Objekte!
Erwartete Ausgabe:
=== REPORT ===
Sales increased by 25%
=============
INVOICE
--------
Widget x3 - $150
Total Due
Dear Reader,
Thank you for your order
SincerelySpickzettel
Das Factory-Muster delegiert die Objekterstellung an eine separate Methode oder Klasse, was es Ihnen ermöglicht, Objekte zu erstellen, ohne deren genaue Klasse anzugeben. Die Erstellungslogik ist zentralisiert und kann basierend auf der Eingabe verschiedene Typen zurückgeben.
Grundstruktur des Factory-Musters:
abstract class Notification {
void send(String message);
}
class EmailNotification implements Notification {
@override
void send(String message) => print('Email: $message');
}
class SMSNotification implements Notification {
@override
void send(String message) => print('SMS: $message');
}
class NotificationFactory {
static Notification create(String type) {
switch (type) {
case 'email':
return EmailNotification();
case 'sms':
return SMSNotification();
default:
return EmailNotification();
}
}
}
void main() {
var notification = NotificationFactory.create('sms');
notification.send('Hello!'); // SMS: Hello!
}Der Client-Code interagiert nur mit der Factory und dem abstrakten Typ, nicht mit den konkreten Implementierungsklassen. Dies macht das Hinzufügen neuer Typen einfach – Sie müssen nur die Factory anpassen.
Verwenden Sie das Factory-Muster, wenn:
- Die Objekterstellung komplexe Logik beinhaltet
- Sie Implementierungsklassen vor Clients verbergen möchten
- Der genaue zu erstellende Typ von Laufzeitbedingungen abhängt
Probier es selbst
import 'document_factory.dart';
void main() {
// TODO: Verwende DocumentFactory.create(), um einen Bericht zu erstellen
// Gib dann das Ergebnis der Generierung mit dem Inhalt "Sales increased by 25%" aus
// TODO: Gib eine Leerzeile aus
// TODO: Verwende DocumentFactory.create(), um eine Rechnung zu erstellen
// Gib dann das Ergebnis der Generierung mit dem Inhalt "Widget x3 - \$150" aus
// TODO: Gib eine Leerzeile aus
// TODO: Verwende DocumentFactory.create(), um einen Brief zu erstellen
// Gib dann das Ergebnis der Generierung mit dem Inhalt "Thank you for your order" aus
}
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 DateienBibliotheken & ImportsEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenInstanzvariablenKonstruktor-GrundlagenWiederholung – Einfacher Taschenrechner4Null-Sicherheit
Einführung in Null-SicherheitNullable vs. Non-NullableDie ? und ! OperatorenLate-Schlüsselwort & Null-SicherheitNull-aware OperatorenNull-Sicherheit in KlassenZusammenfassung – Benutzerprofil-System7Abstrakte Klassen & Interfaces
Abstrakte KlassenAbstrakte MethodenInterfaces in DartImplizite InterfacesImplementieren vs. ErweiternMehrfache InterfacesRückblick – Shape Calculator10Collections & Generics
Überblick über List, Set und MapTypsichere CollectionsGenerische KlassenGenerische MethodenGenerische ConstraintsIterable & IteratorZusammenfassung – Generische Speicherung13Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums mit Methoden16Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen2Konstruktoren in Dart
Standard-KonstruktorBenannte KonstruktorenInitialisierungslistenKonstante KonstruktorenFactory-KonstruktorenWeiterleitende KonstruktorenZusammenfassung – Shape Builder5Kapselung
Öffentliche vs. private MemberDie _ Präfix-KonventionSichtbarkeit auf Library-EbeneGetter & Setter im DetailInformation HidingRückblick – Schülerdatensätze8Mixins
Einführung in MixinsMixins erstellenMehrere Mixins verwendenDas on-Schlüsselwort in MixinsMixin vs. VererbungMixin vs. InterfaceZusammenfassung - Tiersystem11Spezielle Methoden
toString() überschreibenhashCode & == überschreibenComparable-Interfacecall()-MethodenoSuchMethod überschreibenZusammenfassung – Eigene Collection14Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster3Klasseneigenschaften
Instanz- vs. statische MemberFinal & Const FelderLate-VariablenStatische Methoden & FelderGetter und SetterZusammenfassung – Bankkonto-Manager6Vererbung
Grundlagen der VererbungDas super-SchlüsselwortMethoden überschreibenDie @override-AnnotationDas final-Klassen-SchlüsselwortKonstruktoren & VererbungRückblick – Mitarbeiter-Hierarchie9Polymorphismus
Polymorphismus-GrundlagenPolymorphismus über InterfacesTypprüfung zur LaufzeitDie is & as OperatorenDas covariant SchlüsselwortZusammenfassung – Payment Processor