Menu
Coddy logo textTech

Sistema bancario

Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 62 de 64.

challenge icon

Desafío

Difícil

En este desafío, construirás un sistema bancario con múltiples tipos de cuentas, transacciones y un gestor bancario central.

Implementa el sistema bancario completando los elementos TODO en estos archivos:

  • transaction.py - Clase Transaction para registrar actividades financieras
  • account.py - Clase base abstracta Account con funcionalidad común
  • savingsaccount.py - Implementación de SavingsAccount con interés y saldo mínimo
  • checkingaccount.py - Implementación de CheckingAccount con capacidades de sobregiro
  • bank.py - Clase Bank para gestionar cuentas y transferencias

Hoja de referencia

Este desafío consiste en construir un sistema bancario con conceptos de programación orientada a objetos que incluyen:

  • Clase Transaction - Registra actividades financieras
  • Clase abstracta Account - Clase base con funcionalidad de cuenta común
  • SavingsAccount - Tipo de cuenta con cálculo de intereses y requisitos de saldo mínimo
  • CheckingAccount - Tipo de cuenta con capacidades de protección contra sobregiros
  • Clase Bank - Gestor central para cuentas y transferencias de dinero

Los conceptos clave incluyen herencia, abstracción, encapsulación y polimorfismo para crear una arquitectura de sistema bancario modular.

Pruébalo tú mismo

from transaction import Transaction
from account import Account
from savingsaccount import SavingsAccount
from checkingaccount import CheckingAccount
from bank import Bank

# Manejador de casos de prueba integral que cubre todos los escenarios y casos límite
test_case = input()

if test_case == "transaction_class":
    # Probar la clase Transaction
    transaction = Transaction("deposit", 100.50, None)
    print(str(transaction))
    
    transaction = Transaction("withdrawal", 75.25, None)
    print(str(transaction))

elif test_case == "account_deposit":
    # Probar la funcionalidad de depósito
    savings = SavingsAccount("S123", "John Doe", 500)
    
    # Cantidad positiva
    success, message = savings.deposit(200)
    print(f"Success: {success}")
    print(f"Message: {message}")
    print(f"Balance: ${savings.get_balance():.2f}")
    
    # Cantidad negativa (debería fallar)
    success, message = savings.deposit(-50)
    print(f"Success: {success}")
    print(f"Message: {message}")

elif test_case == "savings_withdraw":
    # Probar el retiro de una cuenta de ahorros
    savings = SavingsAccount("S123", "John Doe", 500, min_balance=200)
    
    # Retiro válido
    success, message = savings.withdraw(100)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Inválido (por debajo del saldo mínimo)
    success, message = savings.withdraw(300)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Cantidad negativa
    success, message = savings.withdraw(-50)
    print(f"Success: {success}")
    print(f"Message: {message}")

elif test_case == "checking_withdraw":
    # Probar el retiro de una cuenta corriente
    checking = CheckingAccount("C123", "Jane Smith", 300, overdraft_limit=200)
    
    # Retiro normal
    success, message = checking.withdraw(100)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Retiro con sobregiro
    success, message = checking.withdraw(300)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Excediendo el límite de sobregiro
    success, message = checking.withdraw(200)
    print(f"Success: {success}")
    print(f"Message: {message}")

elif test_case == "interest_application":
    # Probar la aplicación de intereses
    savings = SavingsAccount("S123", "John Doe", 1000, interest_rate=0.05)
    success, message = savings.apply_interest()
    print(f"Success: {success}")
    print(f"Message: {message}")
    print(f"New Balance: ${savings.get_balance():.2f}")
    
    # Verificar el historial de transacciones
    transactions = savings.get_transaction_history()
    print(f"Transaction count: {len(transactions)}")
    print(f"Last transaction: {transactions[-1]}")

elif test_case == "bank_create_account":
    # Probar la creación de cuentas bancarias
    bank = Bank("Test Bank")
    
    # Crear una cuenta de ahorros
    success, message = bank.create_account("savings", "S123", "John Doe", 1000, interest_rate=0.03)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Crear una cuenta corriente
    success, message = bank.create_account("checking", "C456", "Jane Smith", 500, overdraft_limit=300)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Crear con un número de cuenta duplicado
    success, message = bank.create_account("savings", "S123", "Another Person", 200)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Crear con un tipo inválido
    success, message = bank.create_account("invalid", "I789", "Test Person", 100)
    print(f"Success: {success}")
    print(f"Message: {message}")

elif test_case == "bank_transfer":
    # Probar la funcionalidad de transferencia bancaria
    bank = Bank("Transfer Test Bank")
    
    # Crear dos cuentas
    bank.create_account("savings", "S123", "John Doe", 1000, min_balance=100)
    bank.create_account("checking", "C456", "Jane Smith", 500)
    
    # Transferencia válida
    success, message = bank.transfer("S123", "C456", 300)
    print(f"Success: {success}")
    print(f"Message: {message}")
    print(f"Source balance: ${bank.get_account('S123').get_balance():.2f}")
    print(f"Destination balance: ${bank.get_account('C456').get_balance():.2f}")
    
    # Transferencia inválida (excede el saldo mínimo)
    success, message = bank.transfer("S123", "C456", 700)
    print(f"Success: {success}")
    print(f"Message: {message}")
    
    # Cuenta inválida
    success, message = bank.transfer("NONEXISTENT", "C456", 100)
    print(f"Success: {success}")
    print(f"Message: {message}")

elif test_case == "full_banking_workflow":
    # Probar un flujo de trabajo completo
    bank = Bank("Full Workflow Bank")
    
    # Crear cuentas
    bank.create_account("savings", "S123", "John Doe", 1000, interest_rate=0.05, min_balance=200)
    bank.create_account("checking", "C456", "Jane Smith", 500, overdraft_limit=250)
    
    # Realizar depósitos
    savings = bank.get_account("S123")
    checking = bank.get_account("C456")
    
    savings.deposit(300)
    checking.deposit(200)
    
    # Realizar una transferencia
    bank.transfer("S123", "C456", 400)
    
    # Aplicar intereses a los ahorros
    savings.apply_interest()
    
    # Realizar un retiro de la cuenta corriente con sobregiro
    checking.withdraw(900)
    
    # Imprimir el estado final
    print("Final Account States:")
    print(f"Savings (S123): ${savings.get_balance():.2f}")
    print(f"Checking (C456): ${checking.get_balance():.2f}")
    
    print("\nTransaction History (Savings):")
    for transaction in savings.get_transaction_history():
        print(f"- {transaction}")
    
    print("\nTransaction History (Checking):")
    for transaction in checking.get_transaction_history():
        print(f"- {transaction}")

elif test_case == "inheritance_validation_test":
    # Validación integral de la herencia
    objects = []
    if 'Transaction' in locals():
        objects.append(Transaction('test_param', 100, None))
    if 'SavingsAccount' in locals():
        objects.append(SavingsAccount('test_param', 'Test User'))  
    if 'CheckingAccount' in locals():
        objects.append(CheckingAccount('test_param', 'Test User'))
    if 'Bank' in locals():
        objects.append(Bank('test_param'))
    
    for obj in objects:
        print(f'{type(obj).__name__}:')
        print(f'  MRO: {[cls.__name__ for cls in type(obj).__mro__]}')
        print()

elif test_case == "method_overriding_test":
    # Probar el comportamiento de la anulación de métodos
    print('Testing method overriding...')
    # Crear instancias y probar métodos anulados
    if 'SavingsAccount' in locals():
        obj = SavingsAccount('test', 'Test User')
        print(f'SavingsAccount methods work correctly')
    if 'CheckingAccount' in locals():
        obj = CheckingAccount('test', 'Test User')
        print(f'CheckingAccount methods work correctly')

elif test_case == "attribute_access_test":
    # Probar el acceso a atributos
    print('Testing attribute access...')
    if 'Transaction' in locals():
        obj = Transaction('test', 100, None)
        print(f'Transaction attributes accessible')
    if 'SavingsAccount' in locals():
        obj = SavingsAccount('test', 'Test User')
        print(f'SavingsAccount attributes accessible')
    if 'CheckingAccount' in locals():
        obj = CheckingAccount('test', 'Test User')
        print(f'CheckingAccount attributes accessible')
    if 'Bank' in locals():
        obj = Bank('test')
        print(f'Bank attributes accessible')

elif test_case == "boundary_conditions_test":
    # Probar condiciones de contorno y valores límite
    print('Running boundary_conditions_test test...')
    print('Test completed successfully')

elif test_case == "error_handling_test":
    # Probar el manejo de errores y escenarios de excepción
    print('Running error_handling_test test...')
    print('Test completed successfully')

elif test_case == "polymorphic_behavior_test":
    # Probar el comportamiento polimórfico con objetos mixtos
    print('Running polymorphic_behavior_test test...')
    print('Test completed successfully')

elif test_case == "stress_test":
    # Prueba de esfuerzo con múltiples objetos
    import time
    start_time = time.time()
    
    objects = []
    for i in range(50):
        try:
            objects.append(Transaction(f'test_{i}', 100, None))
        except:
            pass  # Manejar errores de creación con elegancia
        try:
            objects.append(SavingsAccount(f'test_{i}', f'User_{i}'))
        except:
            pass  # Manejar errores de creación con elegancia
        try:
            objects.append(CheckingAccount(f'test_{i}', f'User_{i}'))
        except:
            pass  # Manejar errores de creación con elegancia
        try:
            objects.append(Bank(f'test_{i}'))
        except:
            pass  # Manejar errores de creación con elegancia
    
    end_time = time.time()
    print(f'Created {len(objects)} objects')
    print(f'Time taken: {end_time - start_time:.4f} seconds')
    print('Stress test completed')

elif test_case == "comprehensive_validation":
    # Prueba de validación integral
    print('=== Comprehensive Validation Test ===')
    
    # Prueba 1: Creación básica de objetos
    print('1. Basic Object Creation:')
    success_count = 0
    classes = ['Transaction', 'SavingsAccount', 'CheckingAccount', 'Bank']
    
    try:
        obj = Transaction('test', 100, None)
        success_count += 1
        print(f'   Transaction: Created successfully')
    except Exception as e:
        print(f'   Transaction: Creation failed - {e}')
    try:
        obj = SavingsAccount('test', 'Test User')
        success_count += 1
        print(f'   SavingsAccount: Created successfully')
    except Exception as e:
        print(f'   SavingsAccount: Creation failed - {e}')
    try:
        obj = CheckingAccount('test', 'Test User')
        success_count += 1
        print(f'   CheckingAccount: Created successfully')
    except Exception as e:
        print(f'   CheckingAccount: Creation failed - {e}')
    try:
        obj = Bank('test')
        success_count += 1
        print(f'   Bank: Created successfully')
    except Exception as e:
        print(f'   Bank: Creation failed - {e}')
    
    print(f'   Successfully created {success_count}/{len(classes)} classes')
    
    print('=== Validation Complete ===')

Todas las lecciones de Object Oriented Programming