Embedding di più struct
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 37 di 107.
Go ti consente di incorporare più struct in una singola struct, combinando comportamenti provenienti da fonti diverse. È simile all’ereditarietà multipla presente in altri linguaggi, ma utilizza l’approccio alla composizione di Go.
type Logger struct{}
func (l Logger) Log(msg string) string {
return "LOG: " + msg
}
type Notifier struct{}
func (n Notifier) Notify(msg string) string {
return "NOTIFY: " + msg
}
type Service struct {
Name string
Logger
Notifier
}
La struct Service ora ha accesso ai metodi di entrambi i tipi incorporati:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order
}
Quando si incorporano più strutture, può verificarsi un conflitto di denominazione se due tipi incorporati hanno metodi con lo stesso nome. Go non risolve automaticamente questa ambiguità:
type A struct{}
func (A) Greet() string { return "Hello from A" }
type B struct{}
func (B) Greet() string { return "Hello from B" }
type Combined struct {
A
B
}
func main() {
c := Combined{}
// c.Greet() - error de compilación: selector ambiguo
fmt.Println(c.A.Greet()) // Hello from A
fmt.Println(c.B.Greet()) // Hello from B
}
Quando sorgono conflitti, devi specificare esplicitamente il metodo del tipo incorporato che vuoi chiamare, usando il nome del tipo come qualificatore.
Sfida
FacileCostruiamo un sistema di dispositivi per la casa intelligente che combina più funzionalità tramite l’incorporamento di struct. Creerai un dispositivo in grado sia di controllare l’illuminazione sia di riprodurre musica, incorporando tipi di controller separati.
Organizzerai il codice in tre file:
controllers.go: crea due struct di controller indipendenti che forniscono funzionalità diverse:- Un
LightControllercon un campoBrightness(int) e un metodoSetLight(level int) stringche restituisceLight set to [level]% - Un
AudioControllercon un campoVolume(int) e un metodoSetVolume(level int) stringche restituisceVolume set to [level]%
Status() string. Questo crea un conflitto di nomi che dovrai gestire.LightController.Status()dovrebbe restituireBrightness: [Brightness]%eAudioController.Status()dovrebbe restituireVolume: [Volume]%.- Un
device.go: crea una structSmartDevicecon un campoNameche incorpora siaLightControllersiaAudioController. Aggiungi un metodo chiamatoFullStatus() stringche restituisce il nome del dispositivo insieme agli stati di entrambi i controller, risolvendo l’ambiguità chiamando esplicitamente il metodoStatus()di ciascun tipo incorporato.main.go: leggi la configurazione del dispositivo dall’input, crea unoSmartDevice, usa i metodi promossi per impostare i livelli di illuminazione e volume, quindi mostra lo stato completo.
Verranno forniti i seguenti input:
- Riga 1: nome del dispositivo
- Riga 2: livello di luminosità iniziale (intero)
- Riga 3: livello di volume iniziale (intero)
- Riga 4: nuovo livello di luminosità da impostare (intero)
- Riga 5: nuovo livello di volume da impostare (intero)
Il tuo metodo FullStatus() dovrebbe restituire:
[Name] - [LightController.Status()], [AudioController.Status()]Stampa i risultati delle chiamate a SetLight e SetVolume (promossi dai tipi incorporati), quindi stampa lo stato completo.
Ad esempio, dati Living Room Hub, 50, 30, 75 e 60, il tuo output dovrebbe essere:
Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%Nota che SetLight e SetVolume sono accessibili direttamente su SmartDevice tramite la promozione dei metodi, mentre Status() richiede una qualificazione esplicita perché entrambi i tipi incorporati hanno quel metodo.
Provalo tu
package main
import (
"fmt"
)
func main() {
// Leggi l'input
var name string
fmt.Scanln(&name)
var initialBrightness int
fmt.Scanln(&initialBrightness)
var initialVolume int
fmt.Scanln(&initialVolume)
var newBrightness int
fmt.Scanln(&newBrightness)
var newVolume int
fmt.Scanln(&newVolume)
// TODO: Crea uno SmartDevice con il nome e i valori iniziali forniti
// TODO: Usa il metodo promosso SetLight e stampa il risultato
// TODO: Usa il metodo promosso SetVolume e stampa il risultato
// TODO: Stampa lo stato completo usando il metodo FullStatus()
}
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