Osadzanie wielu struktur
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 37 z 107.
Go pozwala osadzać wiele struktur w jednej strukturze, łącząc zachowania z różnych źródeł. Jest to podobne do wielodziedziczenia w innych językach, ale wykorzystuje podejście Go oparte na kompozycji.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
Struktura Service ma teraz dostęp do metod obu osadzonych typów:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: uruchomiono
fmt.Println(s.Notify("new order")) // NOTIFY: nowe zamówienie
}
Podczas osadzania wielu struktur może wystąpić konflikt nazw, jeśli dwa osadzone typy mają metody o tej samej nazwie. Go nie rozstrzyga automatycznie tej niejednoznaczności:
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - błąd kompilacji: niejednoznaczny selektor
fmt.Println(c.A.Greet()) // Witaj od A
fmt.Println(c.B.Greet()) // Witaj od B
}
Gdy pojawiają się konflikty, musisz wyraźnie wskazać, metodę którego typu osadzonego chcesz wywołać, używając nazwy typu jako kwalifikatora.
Wyzwanie
ŁatwyZbudujmy system urządzeń inteligentnego domu, który łączy wiele funkcji dzięki osadzaniu struktur. Utworzysz urządzenie, które może zarówno sterować oświetleniem, jak i odtwarzać muzykę, osadzając w nim osobne typy kontrolerów.
Podzielisz swój kod na trzy pliki:
controllers.go: Utwórz dwie niezależne struktury kontrolerów, które zapewniają różne funkcje:LightControllerz polemBrightness(int) i metodąSetLight(level int) string, która zwracaLight set to [level]%AudioControllerz polemVolume(int) i metodąSetVolume(level int) string, która zwracaVolume set to [level]%
Status() string. Powoduje to konflikt nazw, z którym trzeba będzie sobie poradzić.LightController.Status()powinno zwracaćBrightness: [Brightness]%, aAudioController.Status()powinno zwracaćVolume: [Volume]%.device.go: Utwórz strukturęSmartDevicez polemName, która osadza zarównoLightController, jak iAudioController. Dodaj metodę o nazwieFullStatus() string, która zwraca nazwę urządzenia wraz ze statusem obu kontrolerów, rozwiązując niejednoznaczność przez jawne wywołanie metodyStatus()każdego osadzonego typu.main.go: Odczytaj konfigurację urządzenia z wejścia, utwórzSmartDevice, użyj promowanych metod, aby ustawić poziomy oświetlenia i głośności, a następnie wyświetl pełny status.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Nazwa urządzenia
- Wiersz 2: Początkowy poziom jasności (liczba całkowita)
- Wiersz 3: Początkowy poziom głośności (liczba całkowita)
- Wiersz 4: Nowy poziom jasności do ustawienia (liczba całkowita)
- Wiersz 5: Nowy poziom głośności do ustawienia (liczba całkowita)
Metoda FullStatus() powinna zwracać:
[Name] - [LightController.Status()], [AudioController.Status()]Wyświetl wyniki wywołania SetLight i SetVolume (które są promowane z osadzonych typów), a następnie wyświetl pełny status.
Na przykład dla danych Living Room Hub, 50, 30, 75 i 60 wynik powinien wyglądać następująco:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%Zwróć uwagę, że metody SetLight i SetVolume są bezpośrednio dostępne w SmartDevice dzięki promocji metod, ale Status() wymaga jawnego określenia typu, ponieważ oba osadzone typy mają tę metodę.
Spróbuj swoich sił
package main
import (
"fmt"
)
func main() {
// Wczytaj dane wejściowe
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: Utwórz SmartDevice o podanej nazwie i wartościach początkowych
// TODO: Użyj promowanej metody SetLight i wypisz wynik
// TODO: Użyj promowanej metody SetVolume i wypisz wynik
// TODO: Wypisz pełny stan za pomocą metody FullStatus()
}
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