Kolekcje polimorficzne
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 44 z 107.
Jednym z najpotężniejszych zastosowań interfejsów jest przechowywanie różnych typów w tej samej kolekcji. Fragment typu interfejsowego może przechowywać dowolną wartość spełniającą wymagania tego interfejsu, dzięki czemu możesz grupować powiązane, ale różne typy.
Rozważ sytuację, w której musisz zarządzać różnymi kształtami. Zamiast osobnych wycinków dla każdego typu możesz użyć pojedynczego wycinka typu interfejsowego:
type Shape interface {
Area() float64
}
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 3},
Circle{Radius: 2},
}
for _, s := range shapes {
fmt.Printf("Area: %.2f\n", s.Area())
}
}
Wycinek shapes zawiera zarówno okręgi, jak i prostokąty. Podczas iterowania każdy element reaguje na Area() zgodnie ze swoim rzeczywistym typem. To polimorfizm zastosowany do kolekcji: ta sama pętla obsługuje jednolicie wszystkie typy kształtów.
Kolekcje polimorficzne są niezbędne podczas tworzenia systemów przetwarzających różnorodne elementy: systemu powiadomień wysyłającego e-maile i SMS-y, gry aktualizującej różne typy przeciwników lub procesora dokumentów obsługującego wiele formatów plików. Kolekcja nie przejmuje się konkretnymi typami — ważne jest tylko, aby każdy element zapewniał wymagane zachowanie.
Wyzwanie
ŁatwyZbudujmy system zarządzania zadaniami, który obsługuje różne typy zadań za pośrednictwem ujednoliconego interfejsu. Utworzysz różne typy zadań i przetworzysz je wszystkie razem w polimorficznej kolekcji.
Rozmieścisz swój kod w trzech plikach:
task.go: Zdefiniuj interfejsTask, który wymaga metodySummary() string. Ten interfejs będzie wspólnym kontraktem, który muszą spełniać wszystkie typy zadań.types.go: Utwórz trzy różne typy zadań, z których każdy implementuje interfejsTask:BugFixz polamiID(string) iSeverity(string). MetodaSummary()zwracaBug #[ID] ([Severity])Featurez polamiName(string) iPoints(int). MetodaSummary()zwracaFeature: [Name] - [Points] ptsDocumentationz polemTopic(string). MetodaSummary()zwracaDocs: [Topic]
main.go: Utwórz funkcję o nazwiePrintBacklog, która przyjmuje wycinekTaski wypisuje podsumowanie każdego zadania w osobnym wierszu. Odczytaj szczegóły zadań z danych wejściowych, utwórz po jednym zadaniu każdego typu, zbierz je wszystkie w jednym wycinku[]Taski przekaż go doPrintBacklog.
Zostaną podane następujące dane wejściowe:
- Wiersz 1: ID błędu
- Wiersz 2: Ważność błędu
- Wiersz 3: Nazwa funkcji
- Wiersz 4: Liczba punktów funkcji (liczba całkowita)
- Wiersz 5: Temat dokumentacji
Na przykład dla wartości 1042, critical, Dark Mode, 8 i API Reference wynik powinien wyglądać tak:
Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API ReferenceZaletą tego rozwiązania jest to, że PrintBacklog nie potrzebuje osobnej logiki dla błędów, funkcji ani dokumentacji. Po prostu iteruje po wycinku i wywołuje Summary() dla każdego elementu. Każdy typ zadania zwraca własny, unikatowy format, pokazując, jak polimorficzne kolekcje pozwalają jednolicie przetwarzać różnorodne typy.
Spróbuj swoich sił
package main
import (
"fmt"
)
// TODO: Utwórz funkcję PrintBacklog, która przyjmuje wycinek elementów Task
// i wypisuje podsumowanie każdego zadania w osobnym wierszu
func main() {
// Wczytaj dane wejściowe
var bugID string
var bugSeverity string
var featureName string
var featurePoints int
var docTopic string
fmt.Scanln(&bugID)
fmt.Scanln(&bugSeverity)
fmt.Scanln(&featureName)
fmt.Scanln(&featurePoints)
fmt.Scanln(&docTopic)
// TODO: Utwórz po jednym zadaniu każdego typu (BugFix, Feature, Documentation)
// TODO: Zbierz wszystkie zadania w jednym wycinku []Task
// TODO: Wywołaj PrintBacklog z wycinkiem zadań
}
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ó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