L'interfaccia error
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 52 di 107.
In Go, gli errori vengono gestiti tramite un'interfaccia integrata chiamata error. Questa interfaccia è straordinariamente semplice. Richiede un solo metodo:
type error interface {
Error() string
}Qualsiasi tipo che implementa un metodo Error() che restituisce una stringa soddisfa automaticamente l’interfaccia error. Questo è il sistema di interfacce di Go in azione: non è necessaria alcuna dichiarazione esplicita.
Il modo più comune per creare errori è usare il pacchetto errors:
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}Le funzioni Go in genere restituiscono un errore come ultimo valore restituito. Quando non si verifica alcun errore, restituisci nil. Chi chiama la funzione controlla quindi se l’errore è diverso da nil prima di procedere:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Puoi anche usare fmt.Errorf per creare messaggi di errore formattati:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Questo modello di restituzione degli errori anziché di generazione di eccezioni è fondamentale in Go. Rende esplicita la gestione degli errori e ti costringe a considerare cosa succede quando qualcosa va storto: un aspetto chiave della scrittura di codice robusto e orientato agli oggetti.
Sfida
FacileCostruiamo un sistema di conti bancari che mostri il modello di gestione degli errori di Go. Creerai un conto semplice che convalida le operazioni e restituisce errori significativi quando qualcosa va storto.
Organizzerai il codice in due file:
account.go: crea una structBankAccountcon un campoOwneresportato (string) e un campobalancenon esportato (float64). Implementa quanto segue:NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error)- un costruttore che restituisce un errore con il messaggio"initial deposit must be positive"se il deposito iniziale è zero o negativo. Altrimenti, crea e restituisce il conto.Deposit(amount float64) error- aggiunge denaro al saldo. Restituisce un errore con il messaggio"deposit amount must be positive"se l'importo è zero o negativo.Withdraw(amount float64) error- preleva denaro dal saldo. Restituisce un errore con il messaggio"withdrawal amount must be positive"se l'importo è zero o negativo, oppure"insufficient funds"se il saldo è inferiore all'importo del prelievo.Balance() float64- restituisce il saldo attuale
main.go: leggi i dettagli del conto e gli importi delle transazioni dall'input. Prova a creare un conto ed eseguire le operazioni. Dopo ogni operazione, stampa il messaggio di successo oppure il messaggio di errore.
Verranno forniti i seguenti input:
- Riga 1: nome del titolare
- Riga 2: importo del deposito iniziale
- Riga 3: importo del deposito
- Riga 4: importo del prelievo
Per ogni operazione, stampa il risultato:
- Dopo la creazione del conto:
Account created for [Owner] with balance: $[balance]oppureError: [error message] - Dopo il deposito:
Deposited successfully. New balance: $[balance]oppureError: [error message] - Dopo il prelievo:
Withdrew successfully. New balance: $[balance]oppureError: [error message]
Formatta tutti i valori del saldo con due cifre decimali. Se la creazione del conto non riesce, salta le operazioni rimanenti.
Ad esempio, con Alice, 100, 50 e 200, l'output dovrebbe essere:
Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient fundsE con Bob, -50, 25 e 10, l'output dovrebbe essere:
Error: initial deposit must be positiveProvalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi il nome del titolare
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Leggi il deposito iniziale
initialDepositStr, _ := reader.ReadString('\n')
initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)
// Leggi l'importo da depositare
depositStr, _ := reader.ReadString('\n')
depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)
// Leggi l'importo da prelevare
withdrawStr, _ := reader.ReadString('\n')
withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)
// TODO: Crea un nuovo conto bancario usando NewBankAccount
// Se si verifica un errore, stampa "Error: [error message]" e termina
// Se l'operazione riesce, stampa "Account created for [Owner] with balance: $[balance]"
// TODO: Prova a depositare l'importo indicato
// Se si verifica un errore, stampa "Error: [error message]"
// Se l'operazione riesce, stampa "Deposited successfully. New balance: $[balance]"
// TODO: Prova a prelevare l'importo indicato
// Se si verifica un errore, stampa "Error: [error message]"
// Se l'operazione riesce, stampa "Withdrew successfully. New balance: $[balance]"
// Ricorda di formattare i saldi con due cifre decimali usando fmt.Printf("%.2f", balance)
_ = ownerName
_ = initialDeposit
_ = depositAmount
_ = withdrawAmount
}
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