DMarlon Studio

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.

Pascal · Proyecto: un pequeño banco en DMarlon Studio: el código del ejemplo y su salida en la terminal

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 ... except por operación aísla cada error.
  • Currency es el tipo adecuado para el dinero.
  • Lo que se crea con Create se libera con Free.

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.