Lección 20 · 20 de 20
Proyecto: un pequeño banco · Pascal
Junte clases, herencia, genéricos y excepciones en un pequeño banco en Object Pascal que rechaza retiradas imposibles y libera toda su memoria.

El proyecto final reúne lo esencial del curso en un banco diminuto. `TBank = class(specialize TFPGMap<String, Currency>)` es a la vez clase, herencia y genérico: un mapa de titular a saldo que hereda todo lo de TFPGMap (IndexOf, KeyData, Keys, Data, Count) y le añade la regla de negocio del banco, el método Withdraw.
Withdraw valida antes de tocar nada y, si algo falla, lanza `EBankError`, una excepción propia que hereda de Exception: primero comprueba que exista la cuenta con IndexOf y después que haya saldo suficiente. Solo si todo va bien descuenta el importe y lo anuncia. El tipo `Currency` es el de Pascal para dinero: decimal fijo, sin los errores de redondeo de Double.
El programa abre dos cuentas con bank['Elena'] := 300, que añade la clave o cambia su valor si ya existía, y procesa tres peticiones de 200 EUR. Cada una va en su propio try ... except, así que un rechazo no detiene a las demás: Elena retira, Pablo no tiene saldo suficiente y Marta no tiene cuenta.
Al final se listan los saldos con Keys[i] y Data[i] (sin Sorted, el mapa conserva el orden de alta) y bank.Free libera el mapa con todo su contenido. Las clases de la demo de Odoo sin conexión de DMarlon Studio funcionan exactamente igual: este mismo código, con una ventana de Lazarus delante, sería una aplicación de escritorio, y con el destino Consola se ejecuta con F9.
Código · main.pas
program main;uses SysUtils, fgl;type EBankError = class(Exception); TBank = class(specialize TFPGMap<String, Currency>) // owner -> balance, plus rules procedure Withdraw(const owner: String; amount: Currency); end;procedure TBank.Withdraw(const owner: String; amount: Currency);begin if IndexOf(owner) < 0 then raise EBankError.CreateFmt('there is no account for %s', [owner]); if amount > KeyData[owner] then raise EBankError.CreateFmt('%s has only %.2f', [owner, Double(KeyData[owner])]); KeyData[owner] := KeyData[owner] - amount; WriteLn(owner, ' withdrew ', amount:0:2);end;const Requests: array[0..2] of String = ('Elena', 'Pablo', 'Marta');var bank: TBank; i: Integer;begin bank := TBank.Create; bank['Elena'] := 300; // adds the key, or changes it if it exists bank['Pablo'] := 120; for i := 0 to High(Requests) do try bank.Withdraw(Requests[i], 200); except on E: EBankError do WriteLn('Refused: ', E.Message); end; for i := 0 to bank.Count - 1 do WriteLn(bank.Keys[i]:6, bank.Data[i]:8:2); bank.Free;end.Salida · fpc main.pas && ./main
Elena withdrew 200.00 Refused: Pablo has only 120.00 Refused: there is no account for Marta Elena 100.00 Pablo 120.00
Para recordar
- Una clase puede heredar de un genérico ya especializado.
- Valide primero y lance una excepción propia si algo falla.
- Un
try ... exceptpor operación aísla cada error. Currencyes el tipo adecuado para el dinero.- Lo que se crea con
Createse libera conFree.
Ejercicio
Añada a TBank un método Transfer(src, dst: String; amount: Currency) que use Withdraw y lance EBankError si la cuenta de destino no existe, y pruebe a transferir 50 EUR de Elena a Pablo.