Abstract-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 deren konkrete Typen anzugeben. Anstelle einer einzelnen Factory-Funktion definieren Sie ein Factory-Interface, das mehrere konkrete Factories implementieren, wobei jede ein konsistentes Set verwandter Objekte erzeugt.
Stellen Sie sich 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
}
// Abstraktes Factory-Interface
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 dem Factory-Interface und bleibt dabei unabhängig von konkreten Implementierungen:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Verwenden Sie die Abstract Factory, wenn Ihr System mit mehreren Familien verwandter Produkte arbeiten muss und Sie sicherstellen möchten, dass Komponenten aus derselben Familie zusammen verwendet werden. Sie ist komplexer als eine einfache Factory, greifen Sie also nur darauf zurück, wenn Sie tatsächlich Produktfamilien verwalten müssen.
Aufgabe
EinfachLassen Sie uns ein Möbelhaussystem mit dem Abstract Factory Pattern erstellen! Sie werden Fabriken erstellen, die Familien verwandter Möbelstücke produzieren – jede Fabrik produziert einen konsistenten Stil (Modern oder Vintage) und stellt so sicher, dass Stühle und Tische aus derselben Fabrik ästhetisch zusammenpassen.
Sie werden Ihren Code auf drei Dateien verteilen:
furniture.go: Definieren Sie Ihre Produkt-Interfaces und konkreten Möbeltypen.Erstellen Sie zwei Produkt-Interfaces:
Chairmit einer MethodeSitOn() stringTablemit einer MethodePlaceItems() string
Implementieren Sie zwei Möbelfamilien:
- Modern-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: Definieren Sie Ihr Abstract Factory Interface und die konkreten Fabriken.Erstellen Sie ein
FurnitureFactoryInterface mit zwei Methoden:CreateChair() ChairCreateTable() Table
Implementieren Sie zwei konkrete Fabriken:
ModernFactory, dieModernChairundModernTableproduziertVintageFactory, dieVintageChairundVintageTableproduziert
Erstellen Sie außerdem eine Funktion
GetFactory(style string) FurnitureFactory, die basierend auf dem Stil (modernodervintage) die entsprechende Fabrik zurückgibt. Verwenden Sie standardmäßigModernFactoryfür nicht erkannte Stile.main.go: Verwenden Sie Ihre Fabriken, um Räume einzurichten.Erstellen Sie eine Funktion
FurnishRoom(factory FurnitureFactory) string, die die Fabrik verwendet, um einen Stuhl und einen Tisch zu erstellen, und dann einen formatierten String zurückgibt, der beide Aktionen in separaten Zeilen anzeigt.Lesen Sie eine Stileingabe ein, rufen Sie die entsprechende Fabrik ab, richten Sie einen Raum ein und geben Sie das Ergebnis aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Möbelstil (
modern,vintageoder etwas anderes)
Zum Beispiel, bei der Eingabe:
modernSollte Ihre Ausgabe wie folgt aussehen:
Sitting on a sleek modern chair
Placing items on a glass modern tableUnd bei der Eingabe:
vintageSollte Ihre Ausgabe wie folgt aussehen:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableUnd bei der Eingabe:
rusticSollte Ihre Ausgabe wie folgt aussehen:
Sitting on a sleek modern chair
Placing items on a glass modern tableBeachten Sie, wie die Funktion FurnishRoom über das Interface mit jeder Fabrik zusammenarbeitet – sie weiß nicht, ob sie moderne oder Vintage-Möbel erstellt. Dies stellt sicher, dass alle Möbel in einem Raum aus derselben Familie stammen und ein konsistenter Stil beibehalten wird!
Spickzettel
Das Abstract Factory Pattern (Abstrakte Fabrik) erstellt Familien verwandter Objekte, ohne deren konkrete Typen anzugeben. Es definiert eine Factory-Schnittstelle, die von mehreren konkreten Factories implementiert wird, wobei jede einen konsistenten Satz verwandter Objekte erzeugt.
Struktur
Definieren Sie Produkt-Schnittstellen für jeden Objekttyp:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}Erstellen Sie ein Interface für eine abstrakte Fabrik, das Methoden zur Erstellung der einzelnen Produkttypen deklariert:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Konkrete Implementierungen
Implementieren Sie konkrete Produktfamilien, bei denen jede Familie ein konsistentes Styling oder Verhalten aufweist:
// 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{} }Client-Code
Der Client-Code arbeitet mit dem Factory-Interface und bleibt unabhängig von konkreten Implementierungen:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Wann man es verwendet
Verwenden Sie die Abstrakte Fabrik, wenn Ihr System mit mehreren Familien verwandter Produkte arbeiten muss und Sie sicherstellen möchten, dass Komponenten aus derselben Familie zusammen verwendet werden. Es ist komplexer als eine einfache Factory, verwenden Sie es also nur, wenn Sie tatsächlich Produktfamilien verwalten müssen.
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 zurück, der das Ergebnis von SitOn() in der ersten Zeile und das Ergebnis von PlaceItems() in der zweiten Zeile enthält
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 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