Przesłanianie osadzonych metod
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 39 z 107.
Czasami zewnętrzna struktura musi dostarczyć własną implementację metody, którą ma już osadzony typ. Nazywa się to przesłanianiem i pozwala zastępować promowane metody własnym zachowaniem.
Gdy zdefiniujesz metodę w strukturze zewnętrznej o tej samej nazwie co metoda osadzona, pierwszeństwo ma metoda zewnętrzna:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
Teraz, gdy wywołujesz Speak() na obiekcie Dog, używana jest metoda zewnętrzna:
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - metoda Dog
fmt.Println(d.Animal.Speak()) // Some sound - metoda Animal
}
Osadzona metoda nie zniknęła. Nadal możesz uzyskać do niej dostęp jawnie za pośrednictwem nazwy osadzonego typu. Ten wzorzec jest przydatny, gdy chcesz dostosować zachowanie, zachowując dostęp do oryginalnej implementacji, na przykład aby wywołać ją z poziomu nadpisywanej metody:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Przesłanianie daje Ci kontrolę nad tym, które zachowanie jest udostępniane, a jednocześnie pozwala uzyskać dostęp do oryginalnego za pomocą jawnej kwalifikacji.
Wyzwanie
ŁatwyZbudujmy system powiadomień, w którym różne kanały powiadomień mogą dostosowywać formatowanie i dostarczanie wiadomości. Użyjesz przesłaniania metod, aby poszczególne kanały mogły zastępować domyślne zachowanie, a jednocześnie w razie potrzeby uzyskiwać dostęp do oryginalnej implementacji.
Rozmieścisz kod w trzech plikach:
base.go: Utwórz strukturęBaseNotificationz polamiTitleiPriority(oba typu string). Dodaj do niej metodęFormat() string, która zwraca powiadomienie w standardowym formacie:[Priority] Title.channels.go: Utwórz dwa typy kanałów powiadomień, które osadzająBaseNotificationi przesłaniają metodęFormat, stosując własne implementacje:EmailNotificationz polemRecipient. MetodaFormat()powinna zwracaćEmail to [Recipient]: [Priority] TitleSlackNotificationz polemChannel. MetodaFormat()powinna wywoływać osadzoną metodęBaseNotification.Format()i dodawać na początku wyniku#[Channel]:
main.go: Odczytaj szczegóły powiadomienia z wejścia, utwórz oba typy powiadomień i zademonstruj przesłanianie, wypisując:- Sformatowany wynik
EmailNotification(przy użyciu jego przesłoniętej metody) - Podstawowy format
EmailNotification(przez jawne wywołanie osadzonej metody) - Sformatowany wynik
SlackNotification(który wewnętrznie korzysta z podstawowej metody)
- Sformatowany wynik
Zostaną podane następujące dane wejściowe:
- Wiersz 1: Tytuł powiadomienia
- Wiersz 2: Poziom priorytetu
- Wiersz 3: Odbiorca wiadomości e-mail
- Wiersz 4: Nazwa kanału Slack
Na przykład dla danych Server Alert, HIGH, admin@company.com i ops-alerts wynik powinien wyglądać następująco:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertZwróć uwagę, że EmailNotification całkowicie zastępuje podstawowe formatowanie, podczas gdy SlackNotification rozszerza je, wywołując oryginalną metodę i dodając kontekst kanału. Oba podejścia pokazują różne sposoby efektywnego wykorzystania przesłaniania metod.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Odczytaj tytuł powiadomienia
scanner.Scan()
title := scanner.Text()
// Odczytaj poziom priorytetu
scanner.Scan()
priority := scanner.Text()
// Odczytaj odbiorcę wiadomości e-mail
scanner.Scan()
recipient := scanner.Text()
// Odczytaj nazwę kanału Slack
scanner.Scan()
channel := scanner.Text()
// TODO: Utwórz EmailNotification z bazowym powiadomieniem, tytułem, priorytetem i odbiorcą
// TODO: Utwórz SlackNotification z bazowym powiadomieniem, tytułem, priorytetem i kanałem
// TODO: Wypisz sformatowany wynik EmailNotification (używając jego metody przesłaniającej metodę osadzonego typu)
// TODO: Wypisz bazowy format EmailNotification (jawnie wywołując metodę osadzonego typu)
// TODO: Wypisz sformatowany wynik SlackNotification
fmt.Println() // Element zastępczy – usuń podczas implementacji
}
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