Menu
Coddy logo textTech

Sistema bancario

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 106 de 107.

challenge icon

Desafío

Fácil

¡Vamos a construir un Sistema Bancario que gestione cuentas, transacciones y operaciones financieras! Crearás un sistema robusto donde la integridad de los datos es crítica: los saldos de las cuentas solo deben cambiar a través de métodos validados, y los errores como sobregiros o montos inválidos deben manejarse con elegancia.

Organizarás tu código en cinco archivos:

  • account.go: Define una interfaz Transactable con los métodos Deposit(amount float64) error y Withdraw(amount float64) error. Luego, crea dos tipos de cuenta que implementen esta interfaz:

    Una CheckingAccount con campos no exportados id, holder y balance. Las cuentas corrientes permiten retiros que lleven el saldo exactamente a cero, pero no por debajo.

    Una SavingsAccount con los mismos campos no exportados más un campo minBalance. Las cuentas de ahorro deben mantener un saldo mínimo de 100.0; los retiros que caigan por debajo de este umbral deben fallar.

    Ambos tipos de cuenta necesitan constructores (NewCheckingAccount y NewSavingsAccount) y un método getter Balance() float64. Para los depósitos, rechaza montos negativos o cero. Para los retiros, rechaza montos negativos o cero y montos que excedan los fondos disponibles.

  • transaction.go: Crea un struct Transaction para registrar la actividad de la cuenta. Cada transacción tiene un Type (string: "deposit" o "withdrawal"), Amount (float64) y AccountID (string). Incluye un constructor NewTransaction y un método String() string que devuelva el formato: [Type] $[Amount] on [AccountID] con el monto formateado a dos decimales.
  • bank.go: Construye un struct Bank que gestione múltiples cuentas y registre transacciones. Almacena las cuentas en un mapa por ID y mantén un slice de transacciones. Implementa:
    • NewBank() *Bank — inicializa colecciones vacías
    • AddAccount(id string, account Transactable) — registra una cuenta
    • Deposit(accountID string, amount float64) error — deposita en una cuenta y registra la transacción en caso de éxito
    • Withdraw(accountID string, amount float64) error — retira de una cuenta y registra la transacción en caso de éxito
    • Transfer(fromID, toID string, amount float64) error — mueve dinero entre cuentas de forma atómica (si el retiro tiene éxito pero el depósito falla, revierte el retiro)
    • GetBalance(accountID string) (float64, error) — devuelve el saldo de una cuenta

    Devuelve los errores apropiados para cuentas inexistentes.

  • errors.go: Define errores centinela para tus operaciones bancarias:
    • ErrAccountNotFound — cuando un ID de cuenta no existe
    • ErrInsufficientFunds — cuando el retiro excede el saldo disponible
    • ErrInvalidAmount — cuando el monto del depósito/retiro es cero o negativo
    • ErrMinBalanceRequired — cuando un retiro de ahorros violaría el saldo mínimo
  • main.go: Crea un banco y procesa operaciones basadas en la entrada.

    Lee el número de cuentas a crear. Para cada cuenta, lee el tipo de cuenta (checking o savings), el ID, el nombre del titular y el saldo inicial. Crea el tipo de cuenta apropiado y añádela al banco.

    Luego lee el número de operaciones. Cada operación es una de:

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

    Para cada operación, imprime el resultado: OK para depósitos/retiros/transferencias exitosos, el saldo formateado a dos decimales para consultas de saldo, o Error: [message] para fallos utilizando tus mensajes de error centinela.

Se proporcionarán las siguientes entradas:

  • Número de cuentas, luego el tipo de cada cuenta, ID, titular, saldo inicial (4 líneas cada una)
  • Número de operaciones, luego cada operación (1 línea cada una)

Por ejemplo, dado:

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

Tu salida debería ser:

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

Y dado:

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

Tu salida debería ser:

OK
0.00
Error: invalid amount
Error: account not found

Observa cómo la interfaz Transactable permite al Banco trabajar con ambos tipos de cuenta de manera uniforme, mientras que cada tipo de cuenta impone sus propias reglas. El método Transfer demuestra operaciones atómicas: si algo sale mal a mitad del proceso, ¡el sistema se mantiene consistente!

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Leer el número de cuentas
	scanner.Scan()
	numAccounts, _ := strconv.Atoi(scanner.Text())

	// Crear un nuevo banco
	bank := NewBank()

	// Leer y crear cuentas
	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: Crear el tipo de cuenta apropiado basado en accountType
		// y añadirlo al banco
		_ = accountType
		_ = id
		_ = holder
		_ = balance
	}

	// Leer el número de operaciones
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// Procesar cada operación
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		parts := strings.Fields(scanner.Text())
		operation := parts[0]

		// TODO: Manejar cada tipo de operación:
		// - "deposit": depositar en la cuenta, imprimir "OK" o error
		// - "withdraw": retirar de la cuenta, imprimir "OK" o error
		// - "transfer": transferir entre cuentas, imprimir "OK" o error
		// - "balance": obtener e imprimir el saldo formateado a 2 decimales
		_ = operation
	}
}

Todas las lecciones de Programación Orientada a Objetos