Bankingsystem
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 106 von 107.
Aufgabe
EinfachErstellen wir ein Banksystem, das Konten, Transaktionen und Finanzoperationen verwaltet! Du erstellst ein robustes System, bei dem die Datenintegrität entscheidend ist: Kontostände dürfen sich nur durch validierte Methoden ändern, und Fehler wie Überziehungen oder ungültige Beträge müssen angemessen behandelt werden.
Du organisierst deinen Code über fünf Dateien:
account.go: Definiere einTransactable-Interface mit den MethodenDeposit(amount float64) errorundWithdraw(amount float64) error. Erstelle anschließend zwei Kontotypen, die dieses Interface implementieren:Ein
CheckingAccountmit nicht exportierten Feldernid,holderundbalance. Girokonten erlauben Abhebungen, die den Kontostand genau auf null bringen, aber nicht darunter.Ein
SavingsAccountmit denselben nicht exportierten Feldern sowie einem FeldminBalance. Sparkonten müssen einen Mindestkontostand von 100.0 einhalten: Abhebungen, die den Kontostand unter diesen Schwellenwert senken würden, müssen fehlschlagen.Beide Kontotypen benötigen Konstruktoren (
NewCheckingAccountundNewSavingsAccount) sowie eine Getter-MethodeBalance() float64. Bei Einzahlungen müssen negative oder null Beträge abgelehnt werden. Bei Abhebungen müssen negative oder null Beträge sowie Beträge, die die verfügbaren Mittel überschreiten, abgelehnt werden.transaction.go: Erstelle eineTransaction-Struktur, um Kontoaktivitäten aufzuzeichnen. Jede Transaktion besitzt einenType(string: "deposit" oder "withdrawal"), einenAmount(float64) und eineAccountID(string). Füge einen KonstruktorNewTransactionsowie eine MethodeString() stringhinzu, die das Format[Type] $[Amount] on [AccountID]zurückgibt, wobei der Betrag mit zwei Dezimalstellen formatiert wird.bank.go: Erstelle eineBank-Struktur, die mehrere Konten verwaltet und Transaktionen aufzeichnet. Speichere Konten in einer Map nach ID und verwende ein Slice für die Transaktionen. Implementiere:NewBank() *Bank: initialisiert leere SammlungenAddAccount(id string, account Transactable): registriert ein KontoDeposit(accountID string, amount float64) error: zahlt auf ein Konto ein und zeichnet die Transaktion bei Erfolg aufWithdraw(accountID string, amount float64) error: hebt Geld von einem Konto ab und zeichnet die Transaktion bei Erfolg aufTransfer(fromID, toID string, amount float64) error: überweist Geld atomar zwischen Konten (wenn die Abhebung erfolgreich ist, die Einzahlung jedoch fehlschlägt, wird die Abhebung rückgängig gemacht)GetBalance(accountID string) (float64, error): gibt den Kontostand eines Kontos zurück
Gib für nicht vorhandene Konten geeignete Fehler zurück.
errors.go: Definiere Sentinel-Fehler für deine Bankoperationen:ErrAccountNotFound: wenn eine Konto-ID nicht existiertErrInsufficientFunds: wenn eine Abhebung den verfügbaren Kontostand überschreitetErrInvalidAmount: wenn der Einzahlungs- oder Abhebungsbetrag null oder negativ istErrMinBalanceRequired: wenn eine Abhebung vom Sparkonto den Mindestkontostand verletzen würde
main.go: Erstelle eine Bank und verarbeite Operationen auf Grundlage der Eingabe.Lies die Anzahl der zu erstellenden Konten ein. Lies für jedes Konto den Kontotyp (
checkingodersavings), die ID, den Namen des Inhabers und den Anfangskontostand ein. Erstelle den entsprechenden Kontotyp und füge ihn der Bank hinzu.Lies anschließend die Anzahl der Operationen ein. Jede Operation ist eine der folgenden:
deposit [accountID] [amount]withdraw [accountID] [amount]transfer [fromID] [toID] [amount]balance [accountID]
Gib für jede Operation das Ergebnis aus:
OKfür erfolgreiche Einzahlungen/Abhebungen/Überweisungen, den mit zwei Dezimalstellen formatierten Kontostand für Kontostandabfragen oderError: [message]für Fehler unter Verwendung deiner Sentinel-Fehlermeldungen.
Die folgenden Eingaben werden bereitgestellt:
- Anzahl der Konten, anschließend für jedes Konto Typ, ID, Inhaber und Anfangskontostand (jeweils 4 Zeilen)
- Anzahl der Operationen, anschließend jede Operation (jeweils 1 Zeile)
Beispielsweise bei folgender Eingabe:
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 ACC002Deine Ausgabe sollte folgendermaßen aussehen:
OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00Und bei folgender Eingabe:
1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50Deine Ausgabe sollte folgendermaßen aussehen:
OK
0.00
Error: invalid amount
Error: account not foundBeachte, wie das Transactable-Interface es der Bank ermöglicht, einheitlich mit beiden Kontotypen zu arbeiten, während jeder Kontotyp seine eigenen Regeln durchsetzt. Die Transfer-Methode demonstriert atomare Operationen: Wenn unterwegs etwas schiefgeht, bleibt das System konsistent!
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Anzahl der Konten lesen
scanner.Scan()
numAccounts, _ := strconv.Atoi(scanner.Text())
// Eine neue Bank erstellen
bank := NewBank()
// Konten lesen und erstellen
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: Erstelle den entsprechenden Kontotyp basierend auf accountType
// und füge ihn der Bank hinzu
_ = accountType
_ = id
_ = holder
_ = balance
}
// Anzahl der Operationen lesen
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// Jede Operation verarbeiten
for i := 0; i < numOps; i++ {
scanner.Scan()
parts := strings.Fields(scanner.Text())
operation := parts[0]
// TODO: Behandle jeden Operationstyp:
// - "deposit": Einzahlung auf Konto, "OK" oder Fehler ausgeben
// - "withdraw": Abhebung vom Konto, "OK" oder Fehler ausgeben
// - "transfer": Überweisung zwischen Konten, "OK" oder Fehler ausgeben
// - "balance": Kontostand abrufen und auf 2 Dezimalstellen formatiert ausgeben
_ = operation
}
}
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler