Sistema bancario
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 106 di 107.
Sfida
FacileCostruiamo un sistema bancario che gestisca conti, transazioni e operazioni finanziarie! Creerai un sistema robusto in cui l'integrità dei dati è fondamentale: i saldi dei conti dovrebbero cambiare solo tramite metodi convalidati e gli errori, come gli scoperti o gli importi non validi, devono essere gestiti correttamente.
Organizzerai il codice in cinque file:
account.go: Definisci un'interfacciaTransactablecon i metodiDeposit(amount float64) erroreWithdraw(amount float64) error. Poi crea due tipi di conto che implementano questa interfaccia:Un
CheckingAccountcon i campi non esportatiid,holderebalance. I conti correnti consentono prelievi che portano il saldo esattamente a zero, ma non sotto.Un
SavingsAccountcon gli stessi campi non esportati, più un campominBalance. I conti di risparmio devono mantenere un saldo minimo di 100.0: i prelievi che farebbero scendere il saldo sotto questa soglia devono essere rifiutati.Entrambi i tipi di conto richiedono costruttori (
NewCheckingAccounteNewSavingsAccount) e un metodo getterBalance() float64. Per i depositi, rifiuta gli importi negativi o pari a zero. Per i prelievi, rifiuta gli importi negativi o pari a zero e quelli che superano i fondi disponibili.transaction.go: Crea una structTransactionper registrare l'attività dei conti. Ogni transazione ha unType(string: "deposit" o "withdrawal"), unAmount(float64) e unAccountID(string). Includi un costruttoreNewTransactione un metodoString() stringche restituisca il formato:[Type] $[Amount] on [AccountID], con l'importo formattato con due cifre decimali.bank.go: Crea una structBankche gestisca più conti e registri le transazioni. Memorizza i conti in una mappa indicizzata per ID e conserva una slice di transazioni. Implementa:NewBank() *Bank: inizializza raccolte vuoteAddAccount(id string, account Transactable): registra un contoDeposit(accountID string, amount float64) error: effettua un deposito su un conto e registra la transazione in caso di successoWithdraw(accountID string, amount float64) error: effettua un prelievo da un conto e registra la transazione in caso di successoTransfer(fromID, toID string, amount float64) error: trasferisce denaro tra conti in modo atomico (se il prelievo riesce ma il deposito fallisce, annulla il prelievo)GetBalance(accountID string) (float64, error): restituisce il saldo di un conto
Restituisci gli errori appropriati per i conti inesistenti.
errors.go: Definisci errori sentinella per le operazioni bancarie:ErrAccountNotFound: quando un ID conto non esisteErrInsufficientFunds: quando il prelievo supera il saldo disponibileErrInvalidAmount: quando l'importo del deposito o del prelievo è pari a zero o negativoErrMinBalanceRequired: quando un prelievo dal conto di risparmio violerebbe il saldo minimo
main.go: Crea una banca ed elabora le operazioni in base all'input.Leggi il numero di conti da creare. Per ciascun conto, leggi il tipo di conto (
checkingosavings), l'ID, il nome dell'intestatario e il saldo iniziale. Crea il tipo di conto appropriato e aggiungilo alla banca.Poi leggi il numero di operazioni. Ogni operazione è una tra:
deposit [accountID] [amount]withdraw [accountID] [amount]transfer [fromID] [toID] [amount]balance [accountID]
Per ogni operazione, stampa il risultato:
OKper depositi, prelievi o trasferimenti riusciti, il saldo formattato con due cifre decimali per le richieste del saldo, oppureError: [message]in caso di errore, usando i messaggi dei tuoi errori sentinella.
Verranno forniti i seguenti input:
- Numero di conti, poi per ogni conto il tipo, l'ID, l'intestatario e il saldo iniziale (4 righe ciascuno)
- Numero di operazioni, poi ogni operazione (1 riga ciascuna)
Per esempio, dato:
2
checking
ACC001
Alice
500.00
savings
ACC002
Bob
1000.00
6
deposit ACC001 200
balance ACC001
withdraw ACC002 950
withdraw ACC001 800
transfer ACC001 ACC002 100
balance ACC002L'output dovrebbe essere:
OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00E dato:
1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50L'output dovrebbe essere:
OK
0.00
Error: invalid amount
Error: account not foundNota come l'interfaccia Transactable consenta a Bank di lavorare uniformemente con entrambi i tipi di conto, mentre ogni tipo di conto applica le proprie regole. Il metodo Transfer dimostra le operazioni atomiche: se qualcosa va storto durante il processo, il sistema rimane coerente!
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leggi il numero di conti
scanner.Scan()
numAccounts, _ := strconv.Atoi(scanner.Text())
// Crea una nuova banca
bank := NewBank()
// Leggi e crea i conti
for i := 0; i < numAccounts; i++ {
scanner.Scan()
accountType := scanner.Text()
scanner.Scan()
id := scanner.Text()
scanner.Scan()
holder := scanner.Text()
scanner.Scan()
balance, _ := strconv.ParseFloat(scanner.Text(), 64)
// TODO: Crea il tipo di conto appropriato in base a accountType
// e aggiungilo alla banca
_ = accountType
_ = id
_ = holder
_ = balance
}
// Leggi il numero di operazioni
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// Elabora ogni operazione
for i := 0; i < numOps; i++ {
scanner.Scan()
parts := strings.Fields(scanner.Text())
operation := parts[0]
// TODO: Gestisci ogni tipo di operazione:
// - "deposit": deposita sul conto, stampa "OK" o un errore
// - "withdraw": preleva dal conto, stampa "OK" o un errore
// - "transfer": trasferisci tra conti, stampa "OK" o un errore
// - "balance": ottieni e stampa il saldo formattato con 2 cifre decimali
_ = operation
}
}
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