Polimorfismo tramite interfacce
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 41 di 107.
Polimorfismo consente di trattare tipi diversi in modo uniforme attraverso un'interfaccia condivisa. In Go, questo si ottiene interamente tramite le interfacce, senza ereditarietà o gerarchie di classi.
Quando una funzione accetta un tipo di interfaccia come parametro, è possibile passarle qualsiasi tipo concreto che implementi quell’interfaccia. La funzione non ha bisogno di conoscere il tipo specifico. Le interessa solo il comportamento definito dall’interfaccia:
type Speaker interface {
Speak() string
}
type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }
type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
Ora MakeSound funziona con qualsiasi tipo che abbia un metodo Speak():
func main() {
dog := Dog{Name: "Rex"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // Woof!
MakeSound(cat) // Meow!
}
La stessa chiamata di funzione produce comportamenti diversi a seconda del tipo effettivamente passato. Questa è la polimorfia in azione. La funzione MakeSound viene scritta una sola volta, ma funziona con un numero illimitato di tipi, purché soddisfino l’interfaccia Speaker.
Questo approccio mantiene il tuo codice flessibile ed estensibile. Aggiungere un nuovo tipo che parla non richiede modifiche alle funzioni esistenti: basta implementare l’interfaccia e funzionerà automaticamente.
Sfida
FacileCreiamo un sistema di descrizione dei veicoli che dimostri il polimorfismo in azione. Creerai diversi tipi di veicoli che condividono tutti un comportamento comune tramite un’interfaccia, poi scriverai un’unica funzione che funziona con qualsiasi veicolo.
Organizzerai il codice in due file:
vehicles.go: Definisci un’interfacciaDescriberche richiede un metodoDescribe() string. Poi crea tre tipi di veicoli che implementano ciascuno questa interfaccia a modo proprio:Carcon i campiBrandeModel. Il suo metodoDescribe()restituisceCar: [Brand] [Model]Motorcyclecon i campiBrandeEngineCC(int). Il suo metodoDescribe()restituisceMotorcycle: [Brand] [EngineCC]ccBicyclecon il campoType(come "Mountain" o "Road"). Il suo metodoDescribe()restituisceBicycle: [Type]
main.go: Crea una funzione chiamataPrintDescriptionche accetta qualsiasiDescribere stampa il risultato della chiamata aDescribe(). Leggi i dettagli dei veicoli dall’input, crea un veicolo per ciascuno dei tre tipi e passa ciascuno aPrintDescriptionper dimostrare che la stessa funzione funziona con tutti e tre i tipi diversi.
Verranno forniti i seguenti input:
- Riga 1: marca dell’auto
- Riga 2: modello dell’auto
- Riga 3: marca della motocicletta
- Riga 4: cilindrata del motore della motocicletta in CC (intero)
- Riga 5: tipo di bicicletta
Ad esempio, dati Toyota, Camry, Honda, 600 e Mountain, l’output dovrebbe essere:
Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: MountainNota che PrintDescription non deve sapere se sta ricevendo un’auto, una motocicletta o una bicicletta. Si limita a chiamare Describe() e ciascun tipo risponde con il proprio output univoco. Questo è il polimorfismo: una funzione, comportamenti diversi.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// TODO: Crea una funzione chiamata PrintDescription che accetti qualsiasi Describer
// e stampi il risultato della chiamata a Describe()
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi i dettagli dell'auto
carBrand, _ := reader.ReadString('\n')
carBrand = strings.TrimSpace(carBrand)
carModel, _ := reader.ReadString('\n')
carModel = strings.TrimSpace(carModel)
// Leggi i dettagli della motocicletta
motoBrand, _ := reader.ReadString('\n')
motoBrand = strings.TrimSpace(motoBrand)
motoEngineStr, _ := reader.ReadString('\n')
motoEngineStr = strings.TrimSpace(motoEngineStr)
motoEngine, _ := strconv.Atoi(motoEngineStr)
// Leggi i dettagli della bicicletta
bicycleType, _ := reader.ReadString('\n')
bicycleType = strings.TrimSpace(bicycleType)
// TODO: Crea un Car, un Motorcycle e un Bicycle usando i valori di input
// TODO: Chiama PrintDescription per ogni veicolo per dimostrare il polimorfismo
fmt.Println("TODO: Print vehicle descriptions")
}
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