Wzorzec kompozytu
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 96 z 107.
Wzorzec Composite pozwala traktować pojedyncze obiekty i grupy obiektów w jednolity sposób. Podczas gdy wzorzec State zmienia zachowanie w zależności od stanu wewnętrznego, wzorzec Composite buduje struktury drzewiaste, w których zarówno liście, jak i kontenery współdzielą ten sam interfejs.
Ten wzorzec doskonale sprawdza się w przypadku struktur hierarchicznych, takich jak systemy plików, schematy organizacyjne czy komponenty interfejsu użytkownika. W Go definiujemy wspólny interfejs, który implementują zarówno poszczególne elementy, jak i kontenery:
type Component interface {
GetSize() int
}
type File struct {
Name string
Size int
}
func (f File) GetSize() int {
return f.Size
}Kompozyt (kontener) zawiera elementy podrzędne i implementuje ten sam interfejs, agregując ich wyniki:
type Folder struct {
Name string
Children []Component
}
func (f *Folder) Add(c Component) {
f.Children = append(f.Children, c)
}
func (f Folder) GetSize() int {
total := 0
for _, child := range f.Children {
total += child.GetSize()
}
return total
}Teraz możesz zagnieżdżać foldery w folderach, a wywołanie GetSize() działa identycznie, niezależnie od tego, czy chodzi o pojedynczy plik, czy całe drzewo katalogów:
docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})
root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})
fmt.Println(root.GetSize()) // 175Wzorzec Composite sprawdza się idealnie, gdy chcesz przedstawiać hierarchie część–całość i zależy ci na tym, aby klienci traktowali pojedyncze obiekty i kompozycje identycznie.
Wyzwanie
ŁatwyZbudujmy system schematu organizacyjnego z użyciem wzorca Composite! Utworzysz strukturę, w której zarówno poszczególnych pracowników, jak i działy (zawierające pracowników lub inne działy) można traktować jednolicie — idealne rozwiązanie do obliczania łącznych wynagrodzeń w dowolnej części organizacji.
Rozmieścisz kod w trzech plikach:
component.go: Zdefiniuj wspólny interfejs, który będą implementować zarówno pojedyncze osoby, jak i grupy.Utwórz interfejs
OrgComponentz dwiema metodami:GetSalary() int: zwraca łączne wynagrodzenie tego komponentuGetName() string: zwraca imię i nazwisko pracownika lub nazwę działu
organization.go: Zaimplementuj typy liścia (pojedynczego pracownika) i kompozytu (działu).Utwórz strukturę
Employeez polamiName(string) iSalary(int). Jej metodaGetSalary()zwraca wynagrodzenie pracownika, aGetName()zwraca jego imię i nazwisko.Utwórz strukturę
Departmentz polemName(string) i wycinkiemMembers, który przechowuje elementy typuOrgComponent. Dodaj metodęAdd(c OrgComponent), aby dodawać członków. MetodaGetSalary()powinna zwracać sumę wynagrodzeń wszystkich członków, aGetName()— nazwę działu.main.go: Zbuduj strukturę organizacji i oblicz wynagrodzenia.Wczytaj liczbę pracowników. W przypadku każdego pracownika wczytaj jego imię i nazwisko oraz wynagrodzenie, tworząc instancję
Employee. Następnie wczytaj liczbę działów. W przypadku każdego działu wczytaj jego nazwę i liczbę indeksów członków, a następnie te indeksy (liczone od 0, odnoszące się do wcześniej utworzonych pracowników lub działów w kolejności ich utworzenia).Po zbudowaniu struktury wczytaj końcowy indeks i wypisz nazwę tego komponentu oraz łączne wynagrodzenie w formacie:
[name]: [salary]
Dane wejściowe będą podane w następującej kolejności:
- Liczba pracowników, a następnie imię i nazwisko oraz wynagrodzenie każdego pracownika w osobnych wierszach
- Liczba działów, a następnie nazwa każdego działu, liczba jego członków oraz indeksy członków
- Końcowy indeks do sprawdzenia
Na przykład dla danych:
3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3Wynik powinien być następujący:
Engineering: 165000A dla danych:
4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4Wynik powinien być następujący:
QA: 125000A dla danych:
2
Alice
50000
Bob
60000
0
1Wynik powinien być następujący:
Bob: 60000Zwróć uwagę, że GetSalary() działa identycznie niezależnie od tego, czy wywołujesz ją dla pojedynczego pracownika, czy całego działu: wzorzec Composite pozwala traktować pojedyncze osoby i grupy jednolicie za pomocą tego samego interfejsu!
Spróbuj swoich sił
package main
import "fmt"
func main() {
// Wczytaj liczbę pracowników
var numEmployees int
fmt.Scanln(&numEmployees)
// Przechowuj wszystkie komponenty (pracowników i działy) w kolejności ich tworzenia
var components []OrgComponent
// TODO: Wczytaj imię i wynagrodzenie każdego pracownika
// Utwórz instancje Employee i dodaj je do slice components
for i := 0; i < numEmployees; i++ {
var name string
var salary int
fmt.Scanln(&name)
fmt.Scanln(&salary)
// TODO: Utwórz pracownika i dodaj go do components
}
// Wczytaj liczbę działów
var numDepartments int
fmt.Scanln(&numDepartments)
// TODO: Wczytaj nazwę każdego działu, liczbę członków i ich indeksy
// Utwórz instancje Department, dodaj członków według indeksów i dodaj instancje do components
for i := 0; i < numDepartments; i++ {
var deptName string
var memberCount int
fmt.Scanln(&deptName)
fmt.Scanln(&memberCount)
// TODO: Utwórz dział, wczytaj indeksy członków, dodaj członków i dodaj dział do components
}
// Wczytaj końcowy indeks do wyszukania
var queryIndex int
fmt.Scanln(&queryIndex)
// TODO: Wypisz nazwę komponentu i łączne wynagrodzenie w formacie: [name]: [salary]
// Przykład: fmt.Printf("%s: %d\n", name, salary)
}
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 kolekcja2Typy 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ów11Biblioteka standardowa i OOP
io.Reader i io.Writersort.InterfaceInterfejs fmt.Stringerencoding/json ze strukturamiInterfejs http.HandlerPowtórzenie – modele REST API14Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuMiddleware jako dekorator3Wskaź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