Wzorzec Abstract Factory
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 88 z 107.
Wzorzec Fabryki Abstrakcyjnej rozszerza wzorzec Fabryki, tworząc rodziny powiązanych obiektów bez określania ich konkretnych typów. Zamiast pojedynczej funkcji fabrykującej definiujesz interfejs fabryki, który implementuje wiele konkretnych fabryk, z których każda tworzy spójny zestaw powiązanych obiektów.
Wyobraź sobie zestaw narzędzi UI, który musi tworzyć przyciski i pola wyboru. Różne systemy operacyjne wymagają różnych implementacji, ale komponenty muszą ze sobą spójnie współpracować:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Interfejs fabryki abstrakcyjnej
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Każda konkretna fabryka wytwarza rodzinę powiązanych komponentów:
// Rodzina Windows
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{} }
// Rodzina Mac
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{} }Kod klienta korzysta z interfejsu fabryki, pozostając niezależny od konkretnych implementacji:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Używaj wzorca Abstract Factory, gdy Twój system musi obsługiwać wiele rodzin powiązanych produktów i chcesz mieć pewność, że komponenty z tej samej rodziny będą używane razem. Jest bardziej złożony niż prosty wzorzec Factory, więc sięgaj po niego tylko wtedy, gdy naprawdę potrzebujesz zarządzać rodzinami produktów.
Wyzwanie
ŁatwyStwórzmy system sklepu meblowego, używając wzorca Abstract Factory! Utworzysz fabryki, które produkują rodziny powiązanych ze sobą mebli: każda fabryka produkuje meble w spójnym stylu (Modern lub Vintage), dzięki czemu krzesła i stoły z tej samej fabryki pasują do siebie pod względem estetycznym.
Uporządkujesz swój kod w trzech plikach:
furniture.go: Zdefiniuj interfejsy produktów i konkretne typy mebli.Utwórz dwa interfejsy produktów:
Chairz metodąSitOn() stringTablez metodąPlaceItems() string
Zaimplementuj dwie rodziny mebli:
- Rodzina Modern:
ModernChairzwracaSitting on a sleek modern chair, aModernTablezwracaPlacing items on a glass modern table - Rodzina Vintage:
VintageChairzwracaSitting on an ornate vintage chair, aVintageTablezwracaPlacing items on a wooden vintage table
factory.go: Zdefiniuj interfejs abstrakcyjnej fabryki oraz konkretne fabryki.Utwórz interfejs
FurnitureFactoryz dwiema metodami:CreateChair() ChairCreateTable() Table
Zaimplementuj dwie konkretne fabryki:
ModernFactory, która produkujeModernChairiModernTableVintageFactory, która produkujeVintageChairiVintageTable
Utwórz również funkcję
GetFactory(style string) FurnitureFactory, która zwraca odpowiednią fabrykę na podstawie stylu (modernlubvintage). W przypadku nierozpoznanych stylów domyślnie zwracajModernFactory.main.go: Użyj fabryk, aby umeblować pokoje.Utwórz funkcję
FurnishRoom(factory FurnitureFactory) string, która używa fabryki do utworzenia krzesła i stołu, a następnie zwraca sformatowany ciąg znaków przedstawiający obie czynności w osobnych wierszach.Wczytaj styl, pobierz odpowiednią fabrykę, umebluj pokój i wypisz wynik.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Styl mebli (
modern,vintagelub dowolny inny)
Na przykład dla danych wejściowych:
modernWynik powinien być następujący:
Sitting on a sleek modern chair
Placing items on a glass modern tableA dla danych wejściowych:
vintageWynik powinien być następujący:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableA dla danych wejściowych:
rusticWynik powinien być następujący:
Sitting on a sleek modern chair
Placing items on a glass modern tableZwróć uwagę, że funkcja FurnishRoom współpracuje z dowolną fabryką za pośrednictwem interfejsu. Nie wie, czy tworzy meble w stylu Modern, czy Vintage. Dzięki temu wszystkie meble w pokoju pochodzą z tej samej rodziny i zachowują spójny styl!
Spróbuj swoich sił
package main
import "fmt"
// FurnishRoom używa fabryki do tworzenia mebli i zwraca wynik
// TODO: Zaimplementuj tę funkcję
// - Użyj fabryki do utworzenia krzesła i stołu
// - Zwróć ciąg znaków z wynikiem SitOn() w pierwszym wierszu i wynikiem PlaceItems() w drugim wierszu
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Utwórz krzesło i stół za pomocą fabryki
// TODO: Zwróć sformatowany ciąg znaków z obiema czynnościami w osobnych wierszach
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Pobierz odpowiednią fabrykę na podstawie style
// TODO: Umebluj pokój za pomocą fabryki
// TODO: Wypisz wynik
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja13Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec Abstract FactoryWzorzec ObserverWzorzec Strategy2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online