Factory Pattern
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 53 von 70.
Das Factory-Muster ist ein Erzeugungsmuster, das die Objekterzeugung an eine separate Methode oder Klasse delegiert. Anstatt new direkt in Ihrem Code zu verwenden, lassen Sie eine Factory Objekte für Sie erstellen. Dies zentralisiert die Erzeugungslogik und macht Ihren Code flexibler.
Stellen Sie sich ein Szenario vor, in dem Sie verschiedene Arten von Dokumenten erstellen müssen. Ohne eine Factory wird Ihr Code eng an spezifische Klassen gekoppelt:
// Ohne Factory – verteilte Erstellungslogik
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();Eine Factory kapselt diese Entscheidungsfindung an einem Ort:
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}Jetzt fordert der Client-Code einfach an, was er benötigt, ohne die konkreten Klassen zu kennen:
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // PDF wird geöffnetDas Factory-Pattern glänzt, wenn Sie später neue Typen hinzufügen müssen. Das Hinzufügen eines SpreadsheetDocument erfordert nur Änderungen in der Factory, nicht überall dort, wo Dokumente erstellt werden. Diese Trennung macht Ihre Codebasis einfacher zu warten und zu testen.
Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem mit dem Factory-Pattern erstellen. Anstatt verschiedene Benachrichtigungstypen direkt im gesamten Code zu erstellen, zentralisieren Sie die Erstellungslogik in einer Factory-Klasse, die basierend auf einer einfachen Zeichenfolgenkennung entscheidet, welche Benachrichtigung erstellt werden soll.
Sie werden Ihren Code über drei Dateien organisieren:
Notification.cs: Definieren Sie einINotification-Interface im NamespaceNotificationsmit einer einzigen MethodeSend(string message), die einen String zurückgibt. Erstellen Sie dann drei Klassen, die dieses Interface implementieren:EmailNotification- gibt"Email: {message}"zurückSmsNotification- gibt"SMS: {message}"zurückPushNotification- gibt"Push: {message}"zurück
NotificationFactory.cs: Erstellen Sie eineNotificationFactory-Klasse im selben Namespace mit einerCreate(string type)-Methode, die basierend auf dem Typ-String das entsprechendeINotification-Objekt zurückgibt:"email"gibt eineEmailNotificationzurück"sms"gibt eineSmsNotificationzurück"push"gibt einePushNotificationzurück
EmailNotificationzurück.Program.cs: Führen Sie alles zusammen, indem Sie eine Factory-Instanz erstellen und diese verwenden, um Benachrichtigungen basierend auf der Eingabe zu erstellen. Die Factory übernimmt die gesamte Entscheidungsfindung darüber, welche konkrete Klasse instanziiert werden soll.
Sie erhalten zwei Eingaben:
- Den Benachrichtigungstyp (z. B.
sms) - Die zu sendende Nachricht (z. B.
Your order has shipped)
Verwenden Sie Ihre Factory, um den entsprechenden Benachrichtigungstyp zu erstellen, rufen Sie dann dessen Send-Methode mit der Nachricht auf und geben Sie das Ergebnis aus.
Wenn die Eingaben beispielsweise push und Meeting in 5 minutes sind, sollte die Ausgabe wie folgt lauten:
Push: Meeting in 5 minutesBeachten Sie, dass Ihr Hauptcode niemals new EmailNotification() oder ähnliches verwendet – er fragt einfach die Factory nach dem, was er benötigt. Wenn Sie später eine SlackNotification hinzufügen, müssten Sie nur die Factory aktualisieren und nicht jede Stelle, an der Benachrichtigungen erstellt werden!
Spickzettel
Das Factory-Muster (Fabrikmuster) ist ein Erzeugungsmuster, das die Objekterstellung an eine separate Methode oder Klasse delegiert, wodurch die Erstellungslogik zentralisiert und der Code flexibler wird.
Ohne eine Factory ist die Objekterstellung verstreut und eng gekoppelt:
// Ohne Factory - verstreute Erstellungslogik
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();Mit einer Factory ist die Erstellungslogik an einem Ort gekapselt:
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}Der Client-Code fordert Objekte an, ohne die konkreten Klassen zu kennen:
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // Opening PDFDas Factory-Muster erleichtert das Hinzufügen neuer Typen – Änderungen sind auf die Factory isoliert und nicht über die gesamte Codebasis verteilt.
Probier es selbst
using System;
using Notifications;
class Program
{
public static void Main(string[] args)
{
// Eingabe lesen
string type = Console.ReadLine();
string message = Console.ReadLine();
// TODO: Erstelle eine NotificationFactory-Instanz
// TODO: Verwende die Factory, um die entsprechende Benachrichtigung zu erstellen
// TODO: Rufe die Send-Methode auf und gib das Ergebnis 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 DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjekt-InitialisiererZusammenfassung - Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (:) SyntaxDas 'base'-SchlüsselwortVirtual & Override SchlüsselwörterVersiegelte KlassenDie 'object'-BasisklasseZusammenfassung – Mitarbeiter-Hierarchie7Fortgeschrittene Funktionen
OperatorüberladungIndexer (this[])ToString() überschreibenErweiterungsmethodenZusammenfassung - Benutzerdefinierte Liste10Design Patterns Teil 1
Einführung in Design PatternsThread-sicheres SingletonFactory PatternObserver Pattern (Events)Strategy Pattern2Eigenschaften & Statische Member
Automatisch implementierte EigenschaftenRead-Only- & Write-Only-EigenschaftenStatische Felder & MethodenStatische KlassenExpression-Bodied Member5Polymorphismus & Interfaces
Compile- vs. Runtime-PolymorphismusInterface vs. abstrakte KlasseMehrfache InterfacesExplizite InterfacesUpcasting & DowncastingRecap – Shape Calculator3Klassenarchitektur
Instanz- vs. statische Daten'readonly' & 'const' SchlüsselwörterBacking FieldsZusammenfassung - Bankkonto-Manager6Kapselung
ZugriffsmodifikatorenProperties zur KapselungImplementierung von Data HidingImmutability-MusterRückblick – Studierendendaten