Factory-Muster
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 87 von 107.
Das Factory-Pattern delegiert die Objekterstellung an eine separate Funktion oder Methode, was es Ihnen ermöglicht, Instanzen zu erstellen, ohne die Erstellungslogik offenzulegen. Im Gegensatz zum Singleton, das die Anzahl der Instanzen kontrolliert, konzentriert sich Factory auf die flexible Objekterstellung basierend auf Eingabeparametern.
In Go ist eine Factory typischerweise eine Funktion, die einen Interface-Typ zurückgibt, wodurch Sie verschiedene konkrete Typen über eine einheitliche API erstellen können:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Die Factory-Funktion NewNotifier kapselt die Entscheidung, welcher konkrete Typ erstellt werden soll. Der Client-Code arbeitet nur mit dem Notifier-Interface und bleibt von der spezifischen Implementierung unbeeinflusst:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"Dieses Muster glänzt, wenn Sie Objekte basierend auf Laufzeitbedingungen erstellen müssen, komplexe Initialisierungslogik zentralisieren möchten oder einfach neue Typen hinzufügen müssen, ohne den Client-Code zu ändern. Fügen Sie einfach ein neues Struct hinzu, das das Interface implementiert, und aktualisieren Sie die Factory-Funktion.
Aufgabe
EinfachLassen Sie uns ein Dokumentenkonverter-System mit dem Factory-Muster erstellen! Sie werden eine Factory erstellen, die verschiedene Dokumentenkonverter basierend auf dem Zielformat erzeugt, sodass der Client-Code über eine einheitliche Schnittstelle mit jedem Konverter arbeiten kann.
Sie werden Ihren Code auf zwei Dateien aufteilen:
converter.go: Definieren Sie Ihr Konverter-Interface und die konkreten Implementierungen.Erstellen Sie ein
Converter-Interface mit einer MethodeConvert(content string) string, die den Dokumenteninhalt entgegennimmt und das konvertierte Ergebnis zurückgibt.Implementieren Sie drei Konverter-Typen:
PDFConverter— seineConvert-Methode gibt"PDF: " + contentzurückHTMLConverter— seineConvert-Methode gibt"HTML: <p>" + content + "</p>"zurückMarkdownConverter— seineConvert-Methode gibt"MD: # " + contentzurück
Erstellen Sie eine Factory-Funktion
NewConverter(format string) Converter, die basierend auf dem Format-String ("pdf","html"oder"markdown") den entsprechenden Konverter zurückgibt. Für jedes nicht erkannte Format geben Sie standardmäßig einenPDFConverterzurück.main.go: Verwenden Sie Ihre Factory, um Konverter zu erstellen und Dokumente zu verarbeiten.Lesen Sie einen Formattyp und den Dokumenteninhalt ein. Verwenden Sie die Factory, um den entsprechenden Konverter zu erstellen, konvertieren Sie dann den Inhalt und geben Sie das Ergebnis aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Formattyp (
"pdf","html","markdown"oder etwas anderes) - Zeile 2: Zu konvertierender Dokumenteninhalt
Zum Beispiel, gegeben:
html
Welcome to Go ProgrammingSollte Ihre Ausgabe wie folgt aussehen:
HTML: <p>Welcome to Go Programming</p>Und gegeben:
markdown
Chapter OneSollte Ihre Ausgabe wie folgt aussehen:
MD: # Chapter OneUnd gegeben:
pdf
Annual Report 2024Sollte Ihre Ausgabe wie folgt aussehen:
PDF: Annual Report 2024Und gegeben:
docx
Meeting NotesSollte Ihre Ausgabe wie folgt aussehen:
PDF: Meeting NotesBeachten Sie, dass der Hauptcode nichts über spezifische Konverter-Typen wissen muss — er fragt einfach die Factory nach einem Konverter und verwendet das Interface. Dies macht es einfach, später neue Formate hinzuzufügen, ohne den Client-Code zu ändern!
Spickzettel
Das Factory-Pattern (Fabrikmuster) delegiert die Objekterstellung an eine separate Funktion, was es ermöglicht, Instanzen zu erstellen, ohne die Erstellungslogik offenzulegen. Es konzentriert sich auf die flexible Objekterstellung basierend auf Eingabeparametern.
Eine Factory-Funktion gibt einen Interface-Typ zurück, was die Erstellung verschiedener konkreter Typen über eine einheitliche API ermöglicht:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Der Client-Code arbeitet nur mit dem Interface und bleibt von den spezifischen Implementierungen unberührt:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"Dieses Muster ist nützlich, wenn Sie Objekte basierend auf Laufzeitbedingungen erstellen müssen, komplexe Initialisierungslogik zentralisieren möchten oder neue Typen hinzufügen müssen, ohne den Client-Code zu ändern.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Formattyp lesen
scanner.Scan()
format := scanner.Text()
// Dokumentinhalt lesen
scanner.Scan()
content := scanner.Text()
// TODO: Die Factory verwenden, um den passenden Konverter zu erstellen
// TODO: Den Inhalt konvertieren und das Ergebnis ausgeben
fmt.Println(result)
}
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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterAbstract-Factory-MusterObserver-MusterStrategy-Muster2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool