Grundlagen des Struct Embedding
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 35 von 107.
Struct-Embedding ist Gos Mechanismus, um einen struct-Typ in einen anderen einzuschließen. Anstatt einen Feldnamen zu verwenden, geben Sie einfach den Typ an, und Go reicht die Felder des eingebetteten structs an das äußere struct weiter.
Hier ist die grundlegende Syntax:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // eingebettet - kein Feldname
}
Wenn Sie Address in Person einbetten, können Sie direkt über das äußere Struct auf die eingebetteten Felder zugreifen:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - direkter Zugriff
fmt.Println(p.Country) // UK - direkter Zugriff
fmt.Println(p.Address.City) // funktioniert ebenfalls
}
Beachten Sie, wie p.City funktioniert, ohne explizit p.Address.City zu schreiben. Go macht die Felder des eingebetteten Structs automatisch verfügbar, sodass sie so zugänglich sind, als gehörten sie zum äußeren Struct. Sie können bei Bedarf weiterhin über den Namen des eingebetteten Typs auf sie zugreifen.
Dies unterscheidet sich von regulären verschachtelten Structs, bei denen Sie immer den Feldnamen verwenden müssen, um auf innere Felder zuzugreifen. Embedding erzeugt eine flachere, bequemere API, während die logische Gruppierung verwandter Daten beibehalten wird.
Aufgabe
EinfachLassen Sie uns ein Fahrzeugregistrierungssystem bauen, das Struct-Embedding verwendet, um ein sauberes, organisiertes Datenmodell zu erstellen. Sie werden die Kontaktinformationen von den Fahrzeugdetails trennen und gleichzeitig den direkten Zugriff auf alle Felder durch Einbettung ermöglichen.
Sie werden Ihren Code auf zwei Dateien aufteilen:
vehicle.go: Erstellen Sie zwei Structs, die durch Einbettung zusammenarbeiten:- Ein
ContactInfo-Struct mit den FeldernOwnerNameundPhone(beides Strings) - Ein
Vehicle-Struct, dasContactInfoeinbettet (ohne Feldnamen) und seine eigenen FelderMake,ModelundYearhinzufügt
SummaryfürVehicle, die einen formatierten String mit allen Fahrzeug- und Kontaktinformationen zurückgibt.- Ein
main.go: Lesen Sie Fahrzeug- und Besitzerdetails aus der Eingabe ein, erstellen Sie eineVehicle-Instanz mit eingebettetemContactInfound demonstrieren Sie den Zugriff auf die eingebetteten Felder sowohl direkt als auch über den Namen des eingebetteten Typs. Geben Sie am Ende die Fahrzeugzusammenfassung aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Name des Besitzers
- Zeile 2: Telefonnummer
- Zeile 3: Fahrzeugmarke
- Zeile 4: Fahrzeugmodell
- Zeile 5: Jahr (Ganzzahl)
Ihre Summary-Methode sollte einen String in diesem Format zurückgeben:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]Demonstrieren Sie in Ihrer Hauptdatei die Feld-Promotion (Field Promotion), indem Sie Folgendes ausgeben:
- Den Namen des Besitzers, direkt über das Vehicle aufgerufen (z. B.
v.OwnerName) - Die Telefonnummer, aufgerufen über den eingebetteten Typ (z. B.
v.ContactInfo.Phone) - Die vollständige Zusammenfassung aus Ihrer
Summary-Methode
Zum Beispiel, gegeben Alice Smith, 555-1234, Toyota, Camry und 2023, sollte Ihre Ausgabe wie folgt aussehen:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Beachten Sie, wie beide Zugriffsmuster funktionieren – der direkte Zugriff durch Feld-Promotion und der explizite Zugriff über den Namen des eingebetteten Typs – was Ihnen Flexibilität bei der Arbeit mit eingebetteten Daten gibt.
Spickzettel
Das Einbetten von Structs (Struct embedding) ermöglicht es Ihnen, einen Struct-Typ in einen anderen einzubinden, ohne einen Feldnamen anzugeben. Die Felder des eingebetteten Structs werden in den äußeren Struct "promoted" (hochgestuft), wodurch sie direkt zugänglich sind.
Basissyntax
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // embedded - no field name
}
Zugriff auf eingebettete Felder
Sie können auf zwei Arten auf eingebettete Felder zugreifen:
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
// Direct access (field promotion)
fmt.Println(p.City) // London
// Through embedded type name
fmt.Println(p.Address.City) // London
Go stuft die Felder des eingebetteten Structs automatisch hoch, was den direkten Zugriff ermöglicht, als ob sie zum äußeren Struct gehören würden. Bei Bedarf können Sie weiterhin über den Namen des eingebetteten Typs auf sie zugreifen.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Besitzernamen lesen
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Telefonnummer lesen
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Fahrzeugmarke lesen
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Fahrzeugmodell lesen
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Jahr lesen
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: Erstelle eine Vehicle-Instanz mit eingebettetem ContactInfo
// Verwende die oben aus der Eingabe gelesenen Werte
// TODO: Gib den Besitzernamen aus, auf den direkt über das Vehicle zugegriffen wird (field promotion)
// Beispiel: fmt.Println(v.OwnerName)
// TODO: Gib die Telefonnummer aus, auf die über den Namen des eingebetteten Typs zugegriffen wird
// Beispiel: fmt.Println(v.ContactInfo.Phone)
// TODO: Gib die vollständige Zusammenfassung mit der Summary-Methode aus
// Beispiel: fmt.Println(v.Summary())
// Entferne diese Zeilen, sobald du die Lösung implementiert hast
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
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