Shadowing eingebetteter Methoden
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 39 von 107.
Manchmal müssen Sie für das äußere Struct eine eigene Implementierung einer Methode bereitstellen, die der eingebettete Typ bereits besitzt. Dies wird als Shadowing bezeichnet und ermöglicht es Ihnen, weitergereichte Methoden durch benutzerdefiniertes Verhalten zu überschreiben.
Wenn Sie eine Methode für das äußere Struct mit demselben Namen wie eine eingebettete Methode definieren, hat die äußere Methode Vorrang:
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!"
}
Wenn Sie nun Speak() für einen Dog aufrufen, wird die äußere Methode verwendet:
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - Methode von Dog
fmt.Println(d.Animal.Speak()) // Some sound - Methode von Animal
}
Die eingebettete Methode ist nicht verschwunden. Sie können weiterhin explizit über den Namen des eingebetteten Typs darauf zugreifen. Dieses Muster ist nützlich, wenn Sie das Verhalten anpassen möchten, während die ursprüngliche Implementierung verfügbar bleibt, um sie vielleicht aus Ihrer überschreibenden Methode heraus aufzurufen:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Shadowing gibt Ihnen die Kontrolle darüber, welches Verhalten offengelegt wird, während der Zugriff auf das Original durch explizite Qualifizierung erhalten bleibt.
Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem bauen, bei dem verschiedene Benachrichtigungskanäle anpassen können, wie sie Nachrichten formatieren und zustellen. Sie werden Method Shadowing verwenden, um spezifischen Kanälen zu ermöglichen, das Standardverhalten zu überschreiben, während sie bei Bedarf weiterhin auf die ursprüngliche Implementierung zugreifen können.
Sie werden Ihren Code über drei Dateien organisieren:
base.go: Erstellen Sie einBaseNotification-Struct mit den FeldernTitleundPriority(beides Strings). Geben Sie ihm eineFormat() string-Methode, die die Benachrichtigung in einem Standardformat zurückgibt:[Priority] Title.channels.go: Erstellen Sie zwei Benachrichtigungskanaltypen, dieBaseNotificationeinbetten und dieFormat-Methode mit ihren eigenen Implementierungen überschatten (shadowing):EmailNotificationmit einemRecipient-Feld — seinFormat()sollteEmail to [Recipient]: [Priority] TitlezurückgebenSlackNotificationmit einemChannel-Feld — seinFormat()sollte die eingebetteteBaseNotification.Format()-Methode aufrufen und dem Ergebnis#[Channel]:voranstellen
main.go: Lesen Sie die Benachrichtigungsdetails aus der Eingabe, erstellen Sie beide Arten von Benachrichtigungen und demonstrieren Sie das Shadowing durch das Drucken von:- Der formatierten Ausgabe der
EmailNotification(unter Verwendung ihrer überschatteten Methode) - Dem Basisformat der
EmailNotification(durch explizites Aufrufen der eingebetteten Methode) - Der formatierten Ausgabe der
SlackNotification(die intern die Basismethode verwendet)
- Der formatierten Ausgabe der
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Benachrichtigungstitel
- Zeile 2: Prioritätsstufe
- Zeile 3: E-Mail-Empfänger
- Zeile 4: Slack-Kanalname
Zum Beispiel, gegeben Server Alert, HIGH, admin@company.com und ops-alerts, sollte Ihre Ausgabe wie folgt aussehen:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertBeachten Sie, wie EmailNotification die Basisformatierung vollständig ersetzt, während SlackNotification sie erweitert, indem sie die ursprüngliche Methode aufruft und den Kanalkontext hinzufügt. Beide Ansätze demonstrieren verschiedene Wege, Method Shadowing effektiv einzusetzen.
Spickzettel
Wenn Sie eine Methode für ein äußeres Struct mit demselben Namen wie eine Methode eines eingebetteten Typs definieren, überdeckt (shadows) die äußere Methode die eingebettete Methode:
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!"
}
Beim Aufruf der Methode auf dem äußeren Struct hat die äußere Methode Vorrang:
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - Methode von Dog
fmt.Println(d.Animal.Speak()) // Some sound - Methode von Animal
Die eingebettete Methode bleibt durch explizite Qualifizierung unter Verwendung des Namens des eingebetteten Typs zugänglich. Sie können die ursprüngliche Implementierung aus der überschreibenden Methode heraus aufrufen:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Benachrichtigungstitel lesen
scanner.Scan()
title := scanner.Text()
// Prioritätsstufe lesen
scanner.Scan()
priority := scanner.Text()
// E-Mail-Empfänger lesen
scanner.Scan()
recipient := scanner.Text()
// Slack-Kanalnamen lesen
scanner.Scan()
channel := scanner.Text()
// TODO: Erstelle eine EmailNotification mit der Basis-Benachrichtigung, title, priority und recipient
// TODO: Erstelle eine SlackNotification mit der Basis-Benachrichtigung, title, priority und channel
// TODO: Gib die formatierte Ausgabe der EmailNotification aus (unter Verwendung ihrer überschatteten Methode)
// TODO: Gib das Basisformat der EmailNotification aus (durch explizites Aufrufen der eingebetteten Methode)
// TODO: Gib die formatierte Ausgabe der SlackNotification aus
fmt.Println() // Platzhalter - beim Implementieren entfernen
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool