Each customer has a unique integer key; successive integers are used for each customer.
The bank runs over an extended period. At the start of each day, a bank
teller adds interest to every account and then creates new customers; customers
are never deleted. The ATM then runs all day, handling multiple customers.
Entry of the special key value of 999 into the ATM (at the end of a day)
shuts down the whole system.
There are now two sub-systems in the bank, one where the teller creates accounts and a second where a customer accesses their account through the ATM. The two sub-systems are candidates for new objects, because they encapsulate different functionality and may contain their own data: both the teller and ATM systems use the same set of customers. The teller has to add customers to the set, and the ATM has to find a customer given an input key.
The daily cycle has three parts. The teller creates new accounts. The ATM starts up and continues until either the day end sentinel (666) is input, or the system end sentinel (999) is input. Interest is then added to all accounts. The top level of the daily cycle is thus
from
until day_exit or system_exit
loop
teller
atm
add_interest
end
The teller system contains one loop to create new customers. The ATM system contains two embedded loops. There is a loop around customers, because the ATM handles multiple customers, one at a time. Within each customer, there is a loop around transactions, because the customer can issue as many commands as they desire. The interest code contains one loop to cycle through the customers.
The ATM system gets the customer identifier and password, and checks to
see if they are valid. If they are valid, the account menu is then presented
and the customer uses their account. If the key or password are invalid,
then the customer has to input new values. The key input to the ATM has
three functions: it may identify a customer, it may signal that the ATM
is to be shut down for the day (value 666), or it may close the whole system
down (value 999).
The customers are stored in a LINKED_LIST because they are indexed
by a unique key. The list is created by the BANK, and passed to
the teller and ATM subsystems. Because these two subsystems contain data
(the customers), they deserve to be separate classes. The BANK system
initially makes an empty list, passes the list as an argument to the teller
to create new customers, and then passes the list tot he ATM to process
customer requests.
The client chart for the new part of the system is shown below. The BANK creates and uses a LINKED_LIST of CUSTOMERs, and the two classes TELLER and ATM . These classes use the same list of customers as the BANK. A customer uses a password, and their account via the menu. The suppliers to CUSTOMER have not changed, so that part of the chart is not shown.
The class diagrams for the changed classes in the system are shown below. Processing within a customer is the same as before; in the CUSTOMER class diagram, "INTEGER" has been abbreviated to ‘I’ and "BOOLEAN" to ‘B’ for clarity.
Listings are given below for the classes BANK, TELLER, and
ATM;
where a feature has not changed, only the feature header and comment are
shown. The changed code in CUSTOMER to handle the customer
id
is also shown.
class BANK
creation
make
feature{NONE}
patrons: LINKED_LIST
[CUSTOMER]
teller: TELLER
atm: ATM
make is
-- make the list of customers, pass it to the ATM and TELLER, run the system
do
greeting
!!patrons.make
!!teller.make (patrons)
!!atm.make (patrons)
run
io.putstring ("%NExit banking system%N")
end -- make
feature {NONE}
greeting is
-- welcome the user
run is
-- run the ATM and teller until system is shut down
do
from
until atm.system_finished
loop
teller.run
atm.run
add_interest
end
end -- make
add_interest is
-- add interest to every customer’s account
end -- class BANK
class TELLER
creation {BANK}
make
feature {NONE}
patrons: LINKED_LIST
[CUSTOMER]
feature {BANK}
make (customers: LINKED_LIST
[CUSTOMER]) is
-- set the patrons to the list of customers
do patrons := customers end -- make
run is
-- add interest to all accounts, create new customers
do
show_header
new_customers
end -- run
feature {NONE}
show_header is
-- show the teller a nice message
do
io.putstring ("%N%N*********************")
io.putstring ("%N* Add new customers *")
io.putstring ("%N*********************")
end
new_customers is
-- add any new customers, with unique customer key
local patron: CUSTOMER
do
io.putstring ("%NAdd new customers%N")
from ask_for_more_customers
until no_more
loop
!!patron.make (patrons.count + 1)
patrons.extend (patron)
ask_for_more_customers
end
end -- new_customers
ask_for_more_customers
is
-- ask for more customers, read a reply from the user
do
io.putstring ("%NAny new customers (Y/N)? ")
io.readchar
io.next_line
end-- ask_for_more_customers
no_more: BOOLEAN is
-- has the user said there are no more customers to add?
do
Result := io.lastchar.upper = 'N'
end -- no_more
end -- class TELLER
class ATM
creation {BANK}
make
feature {NONE}
patrons: LINKED_LIST[CUSTOMER]
make (customers: LINKED_LIST[CUSTOMER])
is
-- set the patrons to the list of customers
do patrons := customers end -- make
end_atm: INTEGER is 666
atm_finished: BOOLEAN
is
-- has the ATM finished for the day?
do Result := io.lastint = end_atm end -- atm_finished
end_system: INTEGER is 999
feature {BANK}
run is
-- run the ATM menu until the bank shuts it down
do
from read_id
until atm_finished or system_finished
loop
serve_customer
read_id
end
io.putstring ("%NExiting ATM system%N")
end -- run
system_finished: BOOLEAN
is
-- has the system shutdown code been input?
do
Result := io.lastint = end_system
end -- system_finished
feature {NONE}
read_id is
-- get a customer's user identifier
do
io.putstring ("%NEnter user id: ")
io.readint
end -- read_id
serve_customer is
-- find the customer with the current input id
-- if the customer exists, transfer control
do
find (io.lastint)
if found then patrons.item.login
else io.putstring ("%NThat is not a valid userId")
end
end -- serve_customer
find (id: INTEGER)
is
-- set the cursor at the person with this key
-- if no such person, cursor is offright
do
from patrons.start
until patrons.after or else patrons.item.match
(id)
loop patrons.forth
end
end -- find
found: BOOLEAN is
-- is the cursor in the list?
do
Result := not patrons.after
end -- found
end -- class ATM
class CUSTOMER
...
feature {NONE}
...
id: INTEGER
set_id (key: INTEGER)
is
-- set the id to key
do id := key end -- set_id
show_id is
-- show the customer identifier
do
io.putstring ("%NCustomer #")
io.putint (id)
io.putstring (": ")
end -- show_id
feature {TELLER}
make (id: INTEGER)
is
-- set the customer details
do
...
set_id (key)
end -- make
show is
-- show the customer's personal details
do
show_id
...
end -- show
feature {ATM}
match (id: INTEGER):
BOOLEAN
is
-- does the customer key match this id?
do
Result := id = key
end -- match
end -- class CUSTOMER