Fondamenti dell'embedding delle struct
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 35 di 107.
L’incorporamento di struct è il meccanismo di Go per includere un tipo struct all’interno di un altro. Invece di usare un nome di campo, specifichi semplicemente il tipo e Go rende disponibili i campi della struct incorporata nella struct esterna.
Ecco la sintassi di base:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // incorporato - senza nome del campo
}
Quando incorpori Address in Person, puoi accedere direttamente ai campi incorporati dalla struct esterna:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - acceso directo
fmt.Println(p.Country) // UK - acceso directo
fmt.Println(p.Address.City) // también funciona
}
Nota come p.City funziona senza scrivere esplicitamente p.Address.City. Go promuove automaticamente i campi della struct incorporata, rendendoli accessibili come se appartenessero alla struct esterna. Puoi comunque accedervi tramite il nome del tipo incorporato quando necessario.
Questo è diverso dalle normali struct annidate, in cui devi sempre usare il nome del campo per accedere ai campi interni. L'incorporamento crea un'API più piatta e comoda, mantenendo comunque il raggruppamento logico dei dati correlati.
Sfida
FacileCostruiamo un sistema di registrazione dei veicoli che utilizza l'embedding delle struct per creare un modello dati ordinato e ben organizzato. Separerai le informazioni di contatto dai dettagli del veicolo, consentendo al contempo l'accesso diretto a tutti i campi tramite l'embedding.
Organizzerai il codice in due file:
vehicle.go: crea due struct che collaborano tramite l'embedding:- Una struct
ContactInfocon i campiOwnerNameePhone(entrambi stringhe) - Una struct
Vehicleche incorporaContactInfo(senza un nome di campo) e aggiunge i propri campiMake,ModeleYear
SummarysuVehicleche restituisca una stringa formattata contenente tutte le informazioni sul veicolo e sui contatti.- Una struct
main.go: leggi i dettagli del veicolo e del proprietario dall'input, crea un'istanza diVehicleconContactInfoincorporata e mostra come accedere ai campi incorporati sia direttamente sia tramite il nome del tipo incorporato. Stampa il riepilogo del veicolo alla fine.
Verranno forniti i seguenti input:
- Riga 1: nome del proprietario
- Riga 2: numero di telefono
- Riga 3: marca del veicolo
- Riga 4: modello del veicolo
- Riga 5: anno (intero)
Il tuo metodo Summary dovrebbe restituire una stringa nel seguente formato:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]Nel file principale, mostra la promozione dei campi stampando:
- Il nome del proprietario, accedendovi direttamente tramite Vehicle (ad esempio,
v.OwnerName) - Il numero di telefono, accedendovi tramite il tipo incorporato (ad esempio,
v.ContactInfo.Phone) - Il riepilogo completo restituito dal tuo metodo
Summary
Ad esempio, dati Alice Smith, 555-1234, Toyota, Camry e 2023, l'output dovrebbe essere:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Nota come funzionano entrambi i modi di accesso: l'accesso diretto tramite la promozione dei campi e l'accesso esplicito tramite il nome del tipo incorporato. Questo ti offre flessibilità nel lavorare con i dati incorporati.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi il nome del proprietario
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Leggi il numero di telefono
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Leggi la marca del veicolo
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Leggi il modello del veicolo
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Leggi l'anno
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: Crea un'istanza di Vehicle con ContactInfo incorporato
// Usa i valori letti dall'input qui sopra
// TODO: Stampa il nome del proprietario accedendovi direttamente tramite Vehicle (promozione dei campi)
// Esempio: fmt.Println(v.OwnerName)
// TODO: Stampa il numero di telefono accedendovi tramite il nome del tipo incorporato
// Esempio: fmt.Println(v.ContactInfo.Phone)
// TODO: Stampa il riepilogo completo usando il metodo Summary
// Esempio: fmt.Println(v.Summary())
// Rimuovi queste righe dopo aver implementato la soluzione
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online