Abstraktes Factory-Muster
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 88 von 107.
Das Abstract-Factory-Muster erweitert das Factory-Muster, indem es Familien verwandter Objekte erstellt, ohne ihre konkreten Typen anzugeben. Anstelle einer einzelnen Factory-Funktion definierst du eine Factory-Schnittstelle, die mehrere konkrete Factorys implementieren, wobei jede ein konsistentes Set verwandter Objekte erzeugt.
Stell dir ein UI-Toolkit vor, das Schaltflächen und Kontrollkästchen erstellen muss. Verschiedene Betriebssysteme erfordern unterschiedliche Implementierungen, aber die Komponenten müssen konsistent zusammenarbeiten:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Abstrakte Fabrik-Schnittstelle
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Jede konkrete Fabrik produziert eine Familie verwandter Komponenten:
// Windows-Familie
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Mac-Familie
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Der Client-Code arbeitet mit der Factory-Schnittstelle und bleibt unabhängig von konkreten Implementierungen:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Verwende Abstract Factory, wenn dein System mit mehreren Familien verwandter Produkte arbeiten muss und du sicherstellen möchtest, dass Komponenten derselben Familie zusammen verwendet werden. Sie ist komplexer als eine einfache Factory. Greife daher nur darauf zurück, wenn du tatsächlich Produktfamilien verwalten musst.
Aufgabe
EinfachLass uns ein Möbelladensystem mit dem Abstract-Factory-Muster erstellen! Du wirst Fabriken erstellen, die Familien verwandter Möbelstücke produzieren: Jede Fabrik produziert einen einheitlichen Stil (Modern oder Vintage) und stellt so sicher, dass Stühle und Tische aus derselben Fabrik ästhetisch zusammenpassen.
Du wirst deinen Code auf drei Dateien verteilen:
furniture.go: Definiere deine Produktschnittstellen und konkreten Möbeltypen.Erstelle zwei Produktschnittstellen:
Chairmit einer MethodeSitOn() stringTablemit einer MethodePlaceItems() string
Implementiere zwei Möbelfamilien:
- Moderne Familie:
ModernChairgibtSitting on a sleek modern chairzurück undModernTablegibtPlacing items on a glass modern tablezurück - Vintage-Familie:
VintageChairgibtSitting on an ornate vintage chairzurück undVintageTablegibtPlacing items on a wooden vintage tablezurück
factory.go: Definiere deine abstrakte Fabrikschnittstelle und konkrete Fabriken.Erstelle eine
FurnitureFactory-Schnittstelle mit zwei Methoden:CreateChair() ChairCreateTable() Table
Implementiere zwei konkrete Fabriken:
ModernFactory, dieModernChairundModernTableproduziertVintageFactory, dieVintageChairundVintageTableproduziert
Erstelle außerdem eine Funktion
GetFactory(style string) FurnitureFactory, die abhängig vom Stil (modernodervintage) die passende Fabrik zurückgibt. Verwende für nicht erkannte Stile standardmäßigModernFactory.main.go: Verwende deine Fabriken, um Räume einzurichten.Erstelle eine Funktion
FurnishRoom(factory FurnitureFactory) string, die die Fabrik verwendet, um einen Stuhl und einen Tisch zu erstellen, und anschließend eine formatierte Zeichenkette zurückgibt, die beide Aktionen in separaten Zeilen darstellt.Lies eine Stileingabe ein, rufe die passende Fabrik ab, richte einen Raum ein und gib das Ergebnis aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Möbelstil (
modern,vintageoder etwas anderes)
Zum Beispiel bei:
modernDeine Ausgabe sollte sein:
Sitting on a sleek modern chair
Placing items on a glass modern tableUnd bei:
vintageDeine Ausgabe sollte sein:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableUnd bei:
rusticDeine Ausgabe sollte sein:
Sitting on a sleek modern chair
Placing items on a glass modern tableBeachte, wie die Funktion FurnishRoom über die Schnittstelle mit jeder Fabrik funktioniert. Sie weiß nicht, ob sie moderne oder Vintage-Möbel erstellt. Dadurch wird sichergestellt, dass alle Möbel in einem Raum aus derselben Familie stammen und ein einheitlicher Stil erhalten bleibt!
Probier es selbst
package main
import "fmt"
// FurnishRoom verwendet eine Factory, um Möbel zu erstellen und gibt das Ergebnis zurück
// TODO: Implementiere diese Funktion
// - Verwende die Factory, um einen Stuhl und einen Tisch zu erstellen
// - Gib einen String mit dem SitOn()-Ergebnis in der ersten Zeile und dem PlaceItems()-Ergebnis in der zweiten Zeile zurück
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Erstelle Stuhl und Tisch mithilfe der Factory
// TODO: Gib einen formatierten String mit beiden Aktionen in separaten Zeilen zurück
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Hole die passende Factory basierend auf dem Stil
// TODO: Richte einen Raum mithilfe der Factory ein
// TODO: 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 in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterAbstraktes Factory-MusterObserver-MusterStrategy-Muster2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler