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

¡Construyamos un sistema bancario que administre cuentas, transacciones y operaciones financieras! Crearás un sistema sólido en el que la integridad de los datos es fundamental: los saldos de las cuentas solo deben cambiar mediante métodos validados, y los errores, como los descubiertos o los importes no válidos, deben gestionarse correctamente.

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. Después, crea dos tipos de cuenta que implementen esta interfaz:

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

    Una SavingsAccount con los mismos campos no exportados, además de un campo minBalance. Las cuentas de ahorro deben mantener un saldo mínimo de 100.0: los retiros que hicieran que el saldo bajara de este límite deben fallar.

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

  • transaction.go: Crea una estructura Transaction para registrar la actividad de las cuentas. 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 importe formateado con dos decimales.
  • bank.go: Crea una estructura Bank que administre varias cuentas y registre las transacciones. Almacena las cuentas en un mapa por ID y conserva 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 si la operación tiene éxito
    • Withdraw(accountID string, amount float64) error: retira de una cuenta y registra la transacción si la operación tiene é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 las cuentas inexistentes.

  • errors.go: Define errores centinela para tus operaciones bancarias:
    • ErrAccountNotFound: cuando no existe un ID de cuenta
    • ErrInsufficientFunds: cuando el retiro supera el saldo disponible
    • ErrInvalidAmount: cuando el importe del depósito o retiro es igual a cero o negativo
    • ErrMinBalanceRequired: cuando un retiro de una cuenta de ahorro incumpliría el saldo mínimo
  • main.go: Crea un banco y procesa operaciones según la entrada.

    Lee el número de cuentas que se crearán. 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 correspondiente y añádelo al banco.

    Después, lee el número de operaciones. Cada operación es una de las siguientes:

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

    Para cada operación, imprime el resultado: OK para depósitos, retiros o transferencias exitosos; el saldo formateado con dos decimales para las consultas de saldo; o Error: [message] para los errores, utilizando los mensajes de error centinela.

Se proporcionarán las siguientes entradas:

  • Número de cuentas y, después, el tipo, ID, titular y saldo inicial de cada cuenta (4 líneas por cuenta)
  • Número de operaciones y, después, cada operación (1 línea por operación)

Por ejemplo, dada la siguiente entrada:

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 dada la siguiente entrada:

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 que Bank trabaje de manera uniforme con ambos tipos de cuenta, mientras que cada tipo de cuenta hace cumplir sus propias reglas. El método Transfer demuestra las operaciones atómicas: si algo sale mal durante el proceso, ¡el sistema mantiene su coherencia!

Pruébalo tú mismo

package main

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

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

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

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

	// Lee y crea las 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: Crea el tipo de cuenta apropiado basándote en accountType
		// y añádelo al banco
		_ = accountType
		_ = id
		_ = holder
		_ = balance
	}

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

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

		// TODO: Maneja cada tipo de operación:
		// - "deposit": deposita en la cuenta, imprime "OK" o error
		// - "withdraw": retira de la cuenta, imprime "OK" o error
		// - "transfer": transfiere entre cuentas, imprime "OK" o error
		// - "balance": obtén e imprime el saldo formateado a 2 decimales
		_ = operation
	}
}

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Go online