Grundlagen der Struct-Einbettung
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 35 von 107.
Struct-Einbettung ist der Mechanismus von Go, um einen Struct-Typ in einen anderen einzubinden. Anstatt einen Feldnamen zu verwenden, gibst du einfach den Typ an, und Go stellt die Felder des eingebetteten Structs im äußeren Struct bereit.
Hier ist die grundlegende Syntax:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // eingebettet - kein Feldname
}
Wenn du Address in Person einbettest, kannst du auf die eingebetteten Felder direkt über die äußere Struktur 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 auch
}
Beachte, wie p.City funktioniert, ohne dass du ausdrücklich p.Address.City schreiben musst. Go stellt die Felder der eingebetteten Struktur automatisch bereit, sodass sie zugänglich sind, als würden sie zur äußeren Struktur gehören. Bei Bedarf kannst du weiterhin über den Namen des eingebetteten Typs auf sie zugreifen.
Dies unterscheidet sich von regulären verschachtelten Strukturen, bei denen du immer den Feldnamen verwenden musst, um auf innere Felder zuzugreifen. Einbetten schafft eine flachere, bequemere API und bewahrt gleichzeitig die logische Gruppierung zusammengehöriger Daten.
Aufgabe
EinfachLass uns ein Fahrzeugregistrierungssystem erstellen, das Struct-Einbettung verwendet, um ein sauberes, übersichtliches Datenmodell zu erstellen. Du trennst Kontaktinformationen von Fahrzeugdetails und ermöglichst gleichzeitig den direkten Zugriff auf alle fields durch die Einbettung.
Du organisierst deinen Code über zwei Dateien:
vehicle.go: Erstelle zwei Structs, die durch Einbettung zusammenarbeiten:- Eine
ContactInfo-Struct mit den fieldsOwnerNameundPhone(beide vom Typ String) - Eine
Vehicle-Struct, dieContactInfoeinbettet (ohne field name) und ihre eigenen fieldsMake,ModelundYearhinzufügt
SummaryfürVehicle, die einen formatierten String mit allen Fahrzeug- und Kontaktinformationen zurückgibt.- Eine
main.go: Lies Fahrzeug- und Besitzer details aus der Eingabe ein, erstelle eineVehicle-instance mit eingebettetemContactInfound zeige den Zugriff auf die eingebetteten fields sowohl direkt als auch über den Namen des eingebetteten Typs. Gib am Ende die Fahrzeugzusammenfassung aus.
Die following Eingaben werden bereitgestellt:
- Zeile 1: Besitzername
- Zeile 2: Telefonnummer
- Zeile 3: Fahrzeug-Marke
- Zeile 4: Fahrzeug-Modell
- Zeile 5: Jahr (Ganzzahl)
Deine Summary-Methode sollte einen String in diesem Format zurückgeben:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]Zeige in deiner main-Datei die field promotion durch folgende Ausgaben:
- Den direkt auf dem Vehicle accessed Besitzernamen (z. B.
v.OwnerName) - Die über den eingebetteten Typ accessed Telefonnummer (z. B.
v.ContactInfo.Phone) - Die vollständige Zusammenfassung aus deiner
Summary-Methode
Wenn beispielsweise Alice Smith, 555-1234, Toyota, Camry und 2023 eingegeben werden, sollte deine Ausgabe wie folgt aussehen:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Beachte, wie beide Zugriffsmuster funktionieren: der direkte Zugriff durch field promotion und der explizite Zugriff über den Namen des eingebetteten Typs. Dadurch erhältst du Flexibilität beim Arbeiten mit eingebetteten Daten.
REQUIRED OUTPUT FORMAT:Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Besitzername einlesen
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Telefonnummer einlesen
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Fahrzeugmarke einlesen
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Fahrzeugmodell einlesen
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Jahr einlesen
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, der direkt am Vehicle zugegriffen wird (Feldpromotion)
// Beispiel: fmt.Println(v.OwnerName)
// TODO: Gib die Telefonnummer aus, die über den eingebetteten Typnamen 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 implementierst
_ = 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 OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler