Richard Suchenwirth 2002-10-06 - When
playing OO, memories of an OO design training (in C++ at that time) came up again, and I wanted to test the tiny OO system's ability to handle slightly more complex systems, for example a bank simulation. The code below requires the class* procedures from
Playing OO (with the "uplevel 1" commented out - the GC mechanism appears to be not yet ready for practical use), and besides should be pretty self-explanatory:
#-------------------------------- Definitions
class Bank {} customers {} accounts {} bookings {} atms {}
class Customer {} name "" address "" accounts {}
class Account {} customer "" balance 0.00 sBalance 0.00 bookings {}
class Booking {} date "" amount 0.00 from "" to "" text ""
class ATM {} bank "" location "" content 0.00
proc Bank::newCustomer {self customer} {
$self: lappend customers $customer
set customer
}
proc Bank::newAccount {self customer} {
set no [string range [clock clicks] end-5 end] ;# arbitrary number
Account $no customer $customer
$self: lappend accounts $no
$customer: lappend accounts $no
set no ;# return the generated account number
}
proc Bank::deposit {self account amount} {
if {[$self: lsearch \$accounts $account]<0} {error "no such account"}
if {$amount < 1.00} {error "sorry: minimum deposit $ 1.00"}
set b [Booking #auto date today amount $amount from cash to $account]
$self: lappend bookings $b
$account: lappend bookings $b
$self: set balance [expr [$self: set balance] + $amount]
$account: set balance [expr [$account: set balance] + $amount]
}
proc Bank::withdraw {self account amount {text ""}} {
if {[$self: lsearch \$accounts $account]<0} {error "no such account"}
set have [$account: set balance]
if {$amount > $have} {error "sorry: insufficient funds"}
if {$amount > [$self: set balance]} {error "sorry: we're bankrupt"}
set b [Booking #auto date today amount $amount to cash \
from $account text $text]
$self: lappend bookings $b
$account: lappend bookings $b
$self: set balance [expr [$self: set balance] - $amount]
$account: set balance [expr [$account: set balance] - $amount]
}
proc Bank::transfer {self from to amount {text ""}} {
set b [Booking #auto date today amount $amount from $from \
to $to text $text]
$self: lappend bookings $b
$from: lappend bookings $b
$to: lappend bookings $b
$from: set balance [expr [$from: set balance] - $amount]
$to: set balance [expr [$to: set balance] + $amount]
}
proc Bank::statement {self account} {
set res "\n[$self: set name] * Statement for Account $account"
append res "\nCustomer: [[$account: set customer]: set name]"
set sb [$account: set sBalance]
append res "\n[format %-40s%9.2f {Starting balance:} $sb]"
foreach booking [$account: set bookings] {
set amount [$booking: set amount]
if {[$booking: set from] == $account} {
set amount -$amount
set text "transfer to [$booking: set to]"
} else {
set text "transfer from [$booking: set from]"
}
if {[$booking: set text] != ""} {set text [$booking: set text]}
append res \n[format %-40s%9.2f $text $amount]
}
set balance [$account: set balance]
$account: set sBalance $balance
$account: {set bookings {}} ;# will be printed only one time
append res "\n\n[format %-40s%9.2f {Current balance:} $balance]"
}
proc ATM::refill {self amount} {
$self: set content [expr [$self: set content] + $amount]
}
proc ATM::withdraw {self account amount} {
set content [$self: set content]
if {$amount > $content} {
puts "Sorry, can only provide $content"
set amount $content
}
set text "$self at [$self: set location]"
[$self: set bank] withdraw $account $amount $text
$self: set content [expr [$self: set content] - $amount]
puts "Thank you - come again"
}
Now let's try out the thing with a couple of use cases. We are founding a bank, which has one ATM and soon acquires two customers, a company, and a private person (who happens to work at that company). New customers are registered, and each opens one account, and deposits some initial money. Later, PHB Inc. pays wages via bank transfer, and John Brown goes to the ATM to get some cash, and finally statements are printed for all (two) accounts:
Bank b name "1st National Piggy Bank" balance 100000.00
set atm [ATM #auto bank b location "1 Main St/1600 Elm St"]
b: lappend atms $atm
$atm refill 1500.00
set c [b newCustomer [Customer #auto name "PHB, Inc."]]
set a [b newAccount $c]
b deposit $a 50000.00
set c2 [b newCustomer [Customer #auto name "John Brown"]]
set a2 [b newAccount $c2]
b deposit $a2 500.00
b transfer $a $a2 3500.00 "October wages/JOHN BROWN"
$atm withdraw $a2 120.00
foreach account [b: set accounts] {
puts [b statement $account]
}
The final "printing" of account statements should look like this:
1st National Piggy Bank * Statement for Account 626880
Customer: PHB, Inc.
Starting balance: 0.00
transfer from cash 50000.00
October wages/JOHN BROWN -3500.00
Current balance: 46500.00
1st National Piggy Bank * Statement for Account 626894
Customer: John Brown
Starting balance: 0.00
transfer from cash 500.00
October wages/JOHN BROWN 3500.00
ATM#116 at 1 Main St/1600 Elm St -120.00
Current balance: 3880.00
except for the account numbers... Still a far cry from real banking software, but it was a nice little Sunday evening project ;-)