Accounts
As we start to model our super simple bank simulation, we’re going to begin with the most basic building block, the account. We’re going to start by defining a repository structure for a very simple account that has a balance, an account number, and also a name so we can identify it. Go ahead and open Repository\repository.scm
and add the following structure definition:
STRUCTURE Account DBL ISAM
DESCRIPTION "Bank account information"
FIELD AccountId TYPE INTEGER SIZE 4
FIELD Name TYPE ALPHA SIZE 40
FIELD Balance TYPE DECIMAL SIZE 28 PRECISION 2
END
The field sizes here are pretty arbitrary. Later on in this chapter, we’ll walk through a more in-depth analysis of how to determine the appropriate sizes for some example fields.
The code we’re going to write needs to handle the following operations:
-
Management:
- Account creation: Users can create new bank accounts. Each account will have unique attributes like account number and balance.
- Account details: Users can view details of an account, including the account number and the current balance.
-
Transactions:
- Deposits: Users can deposit money into any account. This involves increasing the account balance by the deposit amount.
- Withdrawals: Users can withdraw money, provided they have sufficient balance. This decreases the account balance.
- Transfers: Users can transfer money from one account to another. This involves withdrawing money from one account and depositing it into another.
-
Reporting:
- Balance inquiry: Users can check the balance of any account.
- Total assets: The application can calculate and display the total assets held across all accounts.
Most of these operations are going to be handled by the Account
class. Let’s go ahead and create a new class in BankApp\Account.dbl
:
namespace BankApp
.include "Account" REPOSITORY, structure="AccountData", end
class Account
private innerData, AccountData
;; Constructor for Account
public method Account
accountId, int
name, string
proc
innerData.AccountId = accountId
innerData.Balance = 0.0
innerData.Name = name
endmethod
;; Method to deposit money
public method Deposit, void
amount, d.
proc
if (amount > 0)
begin
innerData.Balance += amount
end
endmethod
;; Method to withdraw money
public method Withdraw, boolean
amount, d.
proc
if (amount > 0 && innerData.Balance >= amount) then
begin
innerData.Balance -= amount
mreturn true
end
else
mreturn false
endmethod
public property Balance, d28.2
method get
proc
mreturn innerData.Balance
endmethod
endproperty
public property AccountId, int
method get
proc
mreturn innerData.AccountId
endmethod
endproperty
endclass
endnamespace
Now we’ve hidden the details of the account data structure behind the Account
class and its accessors. This is a good start, but we’re going to need to be able to manage the accounts. We are going to be using ArrayList
as our primary data storage mechanism with the account IDs being the index into the ArrayList. This is a very simple way to store our data and is not appropriate for a real-world application. In a later chapter, we’re going to revisit this to use a more appropriate data storage mechanism.
Storing account objects: Each account, represented as an instance of the Account
class, is stored in an ArrayList
. This allows us to dynamically add, remove, and access accounts as needed.
Dynamic resizing: Unlike static arrays, ArrayList
can dynamically resize itself. This is particularly useful for our bank application as the number of accounts can increase over time.
Data persistence: In this version of the application, data is not persisted to disk. When the application is closed, all data is lost.
Onwards to the implementation of the Bank
class that will manage our accounts. Go ahead and create a new class in BankApp\Bank.dbl
:
import System.Collections
namespace BankApp
class Bank
private accounts, @ArrayList
private accountIdOffset, int
;; Constructor for BankApplication
public method Bank
accountIdOffset, int
proc
accounts = new ArrayList()
this.accountIdOffset = accountIdOffset
endmethod
;; Method to create a new account
public method CreateAccount, @Account
name, @string
record
newAccount, @Account
proc
newAccount = new Account(accounts.Count, name)
accounts.Add(newAccount)
mreturn newAccount
endmethod
;; Method to find an account by account number
private method FindAccount, @Account
accountId, int
record
realId, int
proc
realId = accountId - accountIdOffset
mreturn (@Account)accounts[realId]
endmethod
;; Method to deposit money into an account
public method Deposit, void
accountId, int
amount, d.
record
account, @Account
proc
account = FindAccount(accountId)
if (account != ^null)
begin
account.Deposit(amount)
end
endmethod
;; Method to withdraw money from an account
public method Withdraw, boolean
accountId, int
amount, d.
record
account, @Account
proc
account = FindAccount(accountId)
if (account != ^null) then
mreturn account.withdraw(amount)
else
mreturn false
endmethod
;; Method to get account balance
public method GetBalance, d28.2
accountId, int
record
account, @Account
proc
account = FindAccount(accountId)
if (account != ^null) then
begin
mreturn account.Balance
end
else
mreturn 0.0
endmethod
public property TotalAssets, d28.2
method get
record
total, d28.2
proc
total = 0.0
foreach data anAccount in accounts as @Account
begin
total += anAccount.Balance
end
mreturn total
endmethod
endproperty
endclass
endnamespace
The CreateAccount
method in your Bank
class is a factory method. CreateAccount
encapsulates the logic for creating new Account
instances. This ensures that all accounts are created consistently and allows for centralized control over the account creation process. By providing a method to create new accounts for a bank and insert them into the accounts list, you simplify the client code. Users of your Bank
class don’t need to know the details of how accounts are managed; they just call CreateAccount
.
In the future, if the process of account creation becomes more complex (e.g., adding validation or additional steps in account setup), these changes can be made in one place without affecting the client code. This makes your code more maintainable and scalable.
Let’s put it all together in a simple console application. Go ahead and open BankApp\Program.dbl
and add the following code:
import System
import BankApp
main
record
bank, @Bank
proc
bank = new Bank(1000)
bank.CreateAccount("fred");
bank.Deposit(1000, 500.0);
Console.WriteLine("Balance: " + %string(bank.GetBalance(1000)))
bank.Withdraw(1000, 200.0)
Console.WriteLine("Balance after withdrawal: " + %string(bank.GetBalance(1000)))
endmain
Now we can run our application and see the results:
dotnet run
Balance: 500.00
Balance after withdrawal: 300.00
Okay, the building blocks for accounts are in place. In the next section, we’re going to look at how to handle transfers between accounts.