Lesson 20 · 20 of 20
Project: a small bank · Pascal
Bring classes, inheritance, generics and exceptions together in a small Object Pascal bank that refuses impossible withdrawals and frees all its memory.

The final project packs the essentials of the course into a tiny bank. `TBank = class(specialize TFPGMap<String, Currency>)` is class, inheritance and generic all at once: a map from account holder to balance that inherits everything in TFPGMap (IndexOf, KeyData, Keys, Data, Count) and adds the bank's business rule, the Withdraw method.
Withdraw validates before touching anything and, if something is wrong, raises `EBankError`, a custom exception that inherits from Exception: first it checks the account exists with IndexOf, then that the balance is enough. Only when all is well does it take the money out and report it. `Currency` is Pascal's money type: fixed-point decimal, without Double's rounding errors.
The program opens two accounts with bank['Elena'] := 300, which adds the key or changes its value if it already exists, and processes three 200 EUR requests. Each one has its own try ... except, so a refusal does not stop the others: Elena withdraws, Pablo does not have enough and Marta has no account.
Finally the balances are listed with Keys[i] and Data[i] (without Sorted, the map keeps the order in which accounts were opened) and bank.Free releases the map with everything in it. The classes in DMarlon Studio's offline Odoo demo work exactly the same way: put a Lazarus window in front of this code and you have a desktop app, and with the Console target it runs on F9.
Code · 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.Output · 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
Key points
- A class can inherit from an already specialised generic.
- Validate first and raise a custom exception if something is wrong.
- One
try ... exceptper operation keeps each error isolated. Currencyis the right type for money.- What you
Create, youFree.
Exercise
Add a Transfer(src, dst: String; amount: Currency) method to TBank that uses Withdraw and raises EBankError if the destination account does not exist, and try moving 50 EUR from Elena to Pablo.