Implementazione implicita
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 26 di 107.
In molti linguaggi orientati agli oggetti, come Java o C#, devi dichiarare esplicitamente che un tipo implementa un'interfaccia usando parole chiave come implements. Go adotta un approccio diverso: l'implementazione delle interfacce è implicita.
Un tipo soddisfa un'interfaccia semplicemente implementando tutti i suoi metodi. Non è necessario dichiarare la relazione da nessuna parte. Il compilatore la deduce automaticamente.
type Writer interface {
Write(data string) int
}
type FileWriter struct {
Filename string
}
// FileWriter implementa implicitamente Writer
func (f FileWriter) Write(data string) int {
fmt.Println("Writing to", f.Filename)
return len(data)
}
Nota che non c'è una dichiarazione implements Writer su FileWriter. Poiché FileWriter ha un metodo Write(data string) int che corrisponde alla firma dell'interfaccia, soddisfa automaticamente l'interfaccia Writer.
Questo design ha una conseguenza potente: puoi definire interfacce anche dopo che i tipi esistono già. Se una libreria di terze parti ha un tipo con i metodi di cui hai bisogno, puoi creare un'interfaccia che soddisfa già senza modificare il codice originale.
func Save(w Writer, content string) {
w.Write(content)
}
func main() {
fw := FileWriter{Filename: "data.txt"}
Save(fw, "Hello!") // Works because FileWriter satisfies Writer
}
L’implementazione implicita mantiene il codice Go disaccoppiato e flessibile. I tipi non devono conoscere le interfacce in anticipo, il che rende più semplice comporre sistemi a partire da parti indipendenti.
Sfida
FacileCostruiamo un sistema di consegna dei messaggi che dimostri come i tipi soddisfino implicitamente le interfacce in Go. Creerai diversi tipi di messaggero che soddisfano tutti la stessa interfaccia, senza dichiarare esplicitamente questa relazione.
Organizzerai il codice in due file:
messengers.go: definisci un'interfacciaMessengercon un unico metodoSend(message string) string. Poi crea due struct che soddisfino implicitamente questa interfaccia:EmailMessengercon un campoAddress(string)SMSMessengercon un campoPhoneNumber(string)
Sendche restituisce una stringa che descrive la consegna. Nessuna delle due struct dovrebbe dichiarare esplicitamente di implementareMessenger: Go lo dedurrà automaticamente in base alle firme dei metodi.main.go: crea una funzione chiamataDeliverMessageche accetta qualsiasiMessengere una stringa con il messaggio, quindi restituisce il risultato della chiamata aSend. Leggi le informazioni di contatto dall'input, crea entrambi i tipi di messaggero e usa la tua funzione per consegnare i messaggi tramite ciascuno di essi.
Verranno forniti i seguenti input:
- Riga 1: indirizzo email
- Riga 2: numero di telefono
- Riga 3: messaggio da inviare
I tuoi metodi Send dovrebbero restituire stringhe nei seguenti formati:
- EmailMessenger:
Email to [Address]: [message] - SMSMessenger:
SMS to [PhoneNumber]: [message]
Per esempio, dati alice@example.com, 555-1234 e Hello!, il tuo output dovrebbe essere:
Email to alice@example.com: Hello!
SMS to 555-1234: Hello!L'idea fondamentale è che la tua funzione DeliverMessage accetta qualsiasi Messenger e che sia EmailMessenger sia SMSMessenger soddisfano quell'interfaccia semplicemente avendo un metodo Send corrispondente. Non c'è alcuna keyword implements: l'implementazione implicita di Go gestisce tutto.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: Crea una funzione DeliverMessage che accetti un qualsiasi Messenger
// e una stringa di messaggio, poi restituisca il risultato della chiamata a Send
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi l'indirizzo email
email, _ := reader.ReadString('\n')
email = email[:len(email)-1]
// Leggi il numero di telefono
phone, _ := reader.ReadString('\n')
phone = phone[:len(phone)-1]
// Leggi il messaggio
message, _ := reader.ReadString('\n')
if len(message) > 0 && message[len(message)-1] == '\n' {
message = message[:len(message)-1]
}
// TODO: Crea un EmailMessenger con l'indirizzo email
// TODO: Crea un SMSMessenger con il numero di telefono
// TODO: Usa DeliverMessage per inviare il messaggio tramite ciascun Messenger
// e stampa i risultati
}
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