Menu
Coddy logo textTech

Bankensystem

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 106 von 107.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Bankensystem bauen, das Konten, Transaktionen und Finanzoperationen verwaltet! Sie werden ein robustes System erstellen, in dem die Datenintegrität entscheidend ist – Kontostände sollten sich nur über validierte Methoden ändern, und Fehler wie Überziehungen oder ungültige Beträge müssen angemessen behandelt werden.

Sie werden Ihren Code auf fünf Dateien verteilen:

  • account.go: Definieren Sie ein Transactable Interface mit den Methoden Deposit(amount float64) error und Withdraw(amount float64) error. Erstellen Sie dann zwei Kontotypen, die dieses Interface implementieren:

    Ein CheckingAccount mit den nicht exportierten Feldern id, holder und balance. Girokonten erlauben Abhebungen, die den Kontostand auf genau Null, aber nicht darunter bringen.

    Ein SavingsAccount mit denselben nicht exportierten Feldern plus einem minBalance Feld. Sparkonten müssen ein Mindestguthaben von 100.0 verfügen – Abhebungen, die diesen Schwellenwert unterschreiten würden, sollten fehlschlagen.

    Beide Kontotypen benötigen Konstruktoren (NewCheckingAccount und NewSavingsAccount) und eine Balance() float64 Getter-Methode. Lehnen Sie bei Einzahlungen negative Beträge oder Null ab. Lehnen Sie bei Abhebungen negative Beträge, Null oder Beträge, die das verfügbare Guthaben übersteigen, ab.

  • transaction.go: Erstellen Sie ein Transaction Struct, um Kontoaktivitäten aufzuzeichnen. Jede Transaktion hat einen Type (string: "deposit" oder "withdrawal"), einen Amount (float64) und eine AccountID (string). Fügen Sie einen NewTransaction Konstruktor und eine String() string Methode hinzu, die das Format [Type] $[Amount] on [AccountID] zurückgibt, wobei der Betrag auf zwei Dezimalstellen formatiert ist.
  • bank.go: Bauen Sie ein Bank Struct, das mehrere Konten verwaltet und Transaktionen aufzeichnet. Speichern Sie Konten in einer Map nach ID und führen Sie eine Slice von Transaktionen. Implementieren Sie:
    • NewBank() *Bank — initialisiert leere Sammlungen
    • AddAccount(id string, account Transactable) — registriert ein Konto
    • Deposit(accountID string, amount float64) error — zahlt auf ein Konto ein und zeichnet die Transaktion bei Erfolg auf
    • Withdraw(accountID string, amount float64) error — hebt von einem Konto ab und zeichnet die Transaktion bei Erfolg auf
    • Transfer(fromID, toID string, amount float64) error — verschiebt Geld atomar zwischen Konten (wenn die Abhebung gelingt, aber die Einzahlung fehlschlägt, machen Sie die Abhebung rückgängig)
    • GetBalance(accountID string) (float64, error) — gibt den Kontostand eines Kontos zurück

    Geben Sie entsprechende Fehler für nicht existierende Konten zurück.

  • errors.go: Definieren Sie Sentinel-Errors für Ihre Bankoperationen:
    • ErrAccountNotFound — wenn eine Konto-ID nicht existiert
    • ErrInsufficientFunds — wenn die Abhebung das verfügbare Guthaben übersteigt
    • ErrInvalidAmount — wenn der Einzahlungs-/Abhebungsbetrag Null oder negativ ist
    • ErrMinBalanceRequired — wenn eine Sparabhebung das Mindestguthaben verletzen würde
  • main.go: Erstellen Sie eine Bank und verarbeiten Sie Operationen basierend auf der Eingabe.

    Lesen Sie die Anzahl der zu erstellenden Konten ein. Lesen Sie für jedes Konto den Kontotyp (checking oder savings), die ID, den Inhabernamen und das Anfangsguthaben ein. Erstellen Sie den entsprechenden Kontotyp und fügen Sie ihn der Bank hinzu.

    Lesen Sie dann die Anzahl der Operationen ein. Jede Operation ist eine von:

    • deposit [accountID] [amount]
    • withdraw [accountID] [amount]
    • transfer [fromID] [toID] [amount]
    • balance [accountID]

    Geben Sie für jede Operation das Ergebnis aus: OK für erfolgreiche Einzahlungen/Abhebungen/Transfers, den auf zwei Dezimalstellen formatierten Kontostand für Kontostandsabfragen oder Error: [message] für Fehler unter Verwendung Ihrer Sentinel-Errormeldungen.

Die folgenden Eingaben werden bereitgestellt:

  • Anzahl der Konten, dann Typ, ID, Inhaber und Anfangsguthaben jedes Kontos (jeweils 4 Zeilen)
  • Anzahl der Operationen, dann jede Operation (jeweils 1 Zeile)

Zum Beispiel, gegeben:

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 ACC002

Sollte Ihre Ausgabe wie folgt aussehen:

OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00

Und gegeben:

1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50

Sollte Ihre Ausgabe wie folgt aussehen:

OK
0.00
Error: invalid amount
Error: account not found

Beachten Sie, wie das Transactable Interface es der Bank ermöglicht, mit beiden Kontotypen einheitlich zu arbeiten, während jeder Kontotyp seine eigenen Regeln erzwingt. Die Transfer-Methode demonstriert atomare Operationen – wenn auf halbem Weg 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: Den passenden Kontotyp basierend auf accountType erstellen
		// und zur Bank hinzufügen
		_ = 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: Jeden Operationstyp behandeln:
		// - "deposit": auf das Konto einzahlen, "OK" oder Fehler ausgeben
		// - "withdraw": vom Konto abheben, "OK" oder Fehler ausgeben
		// - "transfer": zwischen Konten überweisen, "OK" oder Fehler ausgeben
		// - "balance": Kontostand abrufen und auf 2 Dezimalstellen formatiert ausgeben
		_ = operation
	}
}

Alle Lektionen in Objektorientierte Programmierung