Part 9: Lists

 
 

9.1   Specification

        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.
 
 

9.2   Analysis

        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).
 
 

9.3   Design

        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.
 
 

9.4   Charts

        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.


 
 

9.5   Solution code

        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

end -- class CUSTOMER