J.2. Class ATM
Class ATM (Fig. J.1) represents the ATM as a whole. Lines 511 implement the class's attributes. We determine all but one of these attributes from the UML class diagrams of Fig. 11.21 and Fig. 11.22. Line 5 declares the bool attribute userAuthenticated from Fig. 11.22. Line 6 declares an attribute not found in our UML designint attribute currentAccountNumber, which keeps track of the account number of the current authenticated user. Lines 711 declare reference-type instance variables corresponding to the ATM class's associations modeled in the class diagram of Fig. 11.21. These attributes allow the ATM to access its parts (i.e., its Screen, Keypad, CashDispenser and DepositSlot) and interact with the bank's account information database (i.e., a BankDatabase object).
Figure J.1. Class ATM represents the ATM.
1 // ATM.cs 2 // Represents an automated teller machine. 3 public class ATM 4 { 5 private bool userAuthenticated; // true if user is authenticated 6 private int currentAccountNumber; // user's account number 7 private Screen screen; // reference to ATM's screen 8 private Keypad keypad; // reference to ATM's keypad 9 private CashDispenser cashDispenser; // ref to ATM's cash dispenser 10 private DepositSlot depositSlot; // reference to ATM's deposit slot 11 private BankDatabase bankDatabase; // ref to account info database 12 13 // enumeration that represents main menu options 14 private enum MenuOption 15 { 16 BALANCE_INQUIRY = 1, 17 WITHDRAWAL = 2, 18 DEPOSIT = 3, 19 EXIT_ATM = 4 20 } // end enum MenuOption 21 22 // parameterless constructor initializes instance variables 23 public ATM() 24 { 25 userAuthenticated = false; // user is not authenticated to start 26 currentAccountNumber = 0; // no current account number to start 27 screen = new Screen(); // create screen 28 keypad = new Keypad(); // create keypad 29 cashDispenser = new CashDispenser(); // create cash dispenser 30 depositSlot = new DepositSlot(); // create deposit slot 31 bankDatabase = new BankDatabase(); // create account info database 32 } // end constructor 33 34 // start ATM 35 public void Run() 36 { 37 // welcome and authenticate users; perform transactions 38 while ( true ) // infinite loop 39 { 40 // loop while user is not yet authenticated 41 while ( !userAuthenticated ) 42 { 43 screen.DisplayMessageLine( " Welcome!" ); 44 AuthenticateUser(); // authenticate user 45 } // end while 46 47 PerformTransactions(); // for authenticated user 48 userAuthenticated = false; // reset before next ATM session 49 currentAccountNumber = 0; // reset before next ATM session 50 screen.DisplayMessageLine( " Thank you! Goodbye!" ); 51 } // end while 52 } // end method Run 53 54 // attempt to authenticate user against database 55 private void AuthenticateUser() 56 { 57 // prompt for account number and input it from user 58 screen.DisplayMessage( " Please enter your account number: " ); 59 int accountNumber = keypad.GetInput(); 60 61 // prompt for PIN and input it from user 62 screen.DisplayMessage( " Enter your PIN: " ); 63 int pin = keypad.GetInput(); 64 65 // set userAuthenticated to boolean value returned by database 66 userAuthenticated = 67 bankDatabase.AuthenticateUser( accountNumber, pin ); 68 69 // check whether authentication succeeded 70 if ( userAuthenticated ) 71 currentAccountNumber = accountNumber; // save user's account # 72 else 73 screen.DisplayMessageLine( 74 "Invalid account number or PIN. Please try again." ); 75 } // end method AuthenticateUser 76 77 // display the main menu and perform transactions 78 private void PerformTransactions() 79 { 80 Transaction currentTransaction; // transaction being processed 81 bool userExited = false; // user has not chosen to exit 82 83 // loop while user has not chosen exit option 84 while ( !userExited ) 85 { 86 // show main menu and get user selection 87 int mainMenuSelection = DisplayMainMenu(); 88 89 // decide how to proceed based on user's menu selection 90 switch ( ( MenuOption ) mainMenuSelection ) 91 { 92 // user chooses to perform one of three transaction types 93 case MenuOption.BALANCE_INQUIRY: 94 case MenuOption.WITHDRAWAL: 95 case MenuOption.DEPOSIT: 96 // initialize as new object of chosen type 97 currentTransaction = 98 CreateTransaction( mainMenuSelection ); 99 currentTransaction.Execute(); // execute transaction 100 break; 101 case MenuOption.EXIT_ATM: // user chose to terminate session 102 screen.DisplayMessageLine( " Exiting the system..." ); 103 userExited = true; // this ATM session should end 104 break; 105 default: // user did not enter an integer from 1-4 106 screen.DisplayMessageLine( 107 " You did not enter a valid selection. Try again." ); 108 break; 109 } // end switch 110 } // end while 111 } // end method PerformTransactions 112 113 // display the main menu and return an input selection 114 private int DisplayMainMenu() 115 { 116 screen.DisplayMessageLine( " Main menu:" ); 117 screen.DisplayMessageLine( "1 - View my balance" ); 118 screen.DisplayMessageLine( "2 - Withdraw cash" ); 119 screen.DisplayMessageLine( "3 - Deposit funds" ); 120 screen.DisplayMessageLine( "4 - Exit " ); 121 screen.DisplayMessage( "Enter a choice: " ); 122 return keypad.GetInput(); // return user's selection 123 } // end method DisplayMainMenu 124 125 // return object of specified Transaction derived class 126 private Transaction CreateTransaction( int type ) 127 { 128 Transaction temp = null; // null Transaction reference 129 130 // determine which type of Transaction to create 131 switch ( ( MenuOption ) type ) 132 { 133 // create new BalanceInquiry transaction 134 case MenuOption.BALANCE_INQUIRY: 135 temp = new BalanceInquiry( currentAccountNumber, 136 screen, bankDatabase); 137 break; 138 case MenuOption.WITHDRAWAL: // create new Withdrawal transaction 139 temp = new Withdrawal( currentAccountNumber, screen, 140 bankDatabase, keypad, cashDispenser); 141 break; 142 case MenuOption.DEPOSIT: // create new Deposit transaction 143 temp = new Deposit( currentAccountNumber, screen, 144 bankDatabase, keypad, depositSlot); 145 break; 146 } // end switch 147 148 return temp; 149 } // end method CreateTransaction 150 } // end class ATM |
Lines 1420 declare an enumeration that corresponds to the four options in the ATM's main menu (i.e., balance inquiry, withdrawal, deposit and exit). Lines 2332 declare class ATM's constructor, which initializes the class's attributes. When an ATM object is first created, no user is authenticated, so line 25 initializes userAuthenticated to false. Line 26 initializes currentAccountNumber to 0 because there is no current user yet. Lines 2730 instantiate new objects to represent the parts of the ATM. Recall that class ATM has composition relationships with classes Screen, Keypad, CashDispenser and DepositSlot, so class ATM is responsible for their creation. Line 31 creates a new BankDatabase. As you will soon see, the BankDatabase creates two Account objects that can be used to test the ATM. [Note: If this were a real ATM system, the ATM class would receive a reference to an existing database object created by the bank. However, in this implementation, we are only simulating the bank's database, so class ATM creates the BankDatabase object with which it interacts.]
Implementing the Operation
The class diagram of Fig. 11.22 does not list any operations for class ATM. We now implement one operation (i.e., public method) in class ATM that allows an external client of the class (i.e., class ATMCaseStudy; Section J.13) to tell the ATM to run. ATM method Run (lines 3552) uses an infinite loop (lines 3851) to repeatedly welcome a user, attempt to authenticate the user and, if authentication succeeds, allow the user to perform transactions. After an authenticated user performs the desired transactions and exits, the ATM resets itself, displays a goodbye message and restarts the process for the next user. We use an infinite loop here to simulate the fact that an ATM appears to run continuously until the bank turns it off (an action beyond the user's control). An ATM user can exit the system, but cannot turn off the ATM completely.
Inside method Run's infinite loop, lines 4145 cause the ATM to repeatedly welcome and attempt to authenticate the user as long as the user has not been authenticated (i.e., the condition !userAuthenticated is true). Line 43 invokes method DisplayMessageLine of the ATM's screen to display a welcome message. Like Screen method DisplayMessage designed in the case study, method DisplayMessageLine (declared in lines 1417 of Fig. J.2) displays a message to the user, but this method also outputs a newline after displaying the message. We add this method during implementation to give class Screen's clients more control over the placement of displayed messages. Line 44 invokes class ATM's private utility method AuthenticateUser (declared in lines 5575) to attempt to authenticate the user.
Figure J.2. Class Screen represents the screen of the ATM.
(This item is displayed on pages 1534 - 1535 in the print version)
1 // Screen.cs 2 // Represents the screen of the ATM 3 using System; 4 5 public class Screen 6 { 7 // displays a message without a terminating carriage return 8 public void DisplayMessage( string message ) 9 { 10 Console.Write( message ); 11 } // end method DisplayMessage 12 13 // display a message with a terminating carriage return 14 public void DisplayMessageLine( string message ) 15 { 16 Console.WriteLine( message ); 17 } // end method DisplayMessageLine 18 19 // display a dollar amount 20 public void DisplayDollarAmount( decimal amount ) 21 { 22 Console.Write( "{0:C}", amount ); 23 } // end method DisplayDollarAmount 24 } // end class Screen |
Authenticating the User
We refer to the requirements document to determine the steps necessary to authenticate the user before allowing transactions to occur. Line 58 of method AuthenticateUser invokes method DisplayMessage of the ATM's screen to prompt the user to enter an account number. Line 59 invokes method GetInput of the ATM's keypad to obtain the user's input, then stores this integer in local variable accountNumber. Method AuthenticateUser next prompts the user to enter a PIN (line 62), and stores the PIN in local variable pin (line 63). Next, lines 6667 attempt to authenticate the user by passing the accountNumber and pin entered by the user to the bankDatabase's AuthenticateUser method. Class ATM sets its userAuthenticated attribute to the bool value returned by this methoduserAuthenticated becomes true if authentication succeeds (i.e., the accountNumber and pin match those of an existing Account in bankDatabase) and remains false otherwise. If userAuthenticated is true, line 71 saves the account number entered by the user (i.e., accountNumber) in the ATM attribute currentAccountNumber. The other methods of class ATM use this variable whenever an ATM session requires access to the user's account number. If userAuthenticated is false, lines 7374 call the screen's DisplayMessageLine method to indicate that an invalid account number and/or PIN was entered, so the user must try again. Note that we set currentAccountNumber only after authenticating the user's account number and the associated PINif the database cannot authenticate the user, currentAccountNumber remains 0.
After method Run attempts to authenticate the user (line 44), if userAuthenticated is still false (line 41), the while loop body (lines 4145) executes again. If userAuthenticated is now true, the loop terminates, and control continues with line 47, which calls class ATM's private utility method PerformTransactions.
Performing Transactions
Method PerformTransactions (lines 78111) carries out an ATM session for an authenticated user. Line 80 declares local variable transaction, to which we assign a BalanceInquiry, Withdrawal or Deposit object representing the ATM transaction currently being processed. Note that we use a transaction variable here to allow us to take advantage of polymorphism. Also, note that we name this variable after the role name included in the class diagram of Fig. 4.21currentTransaction. Line 81 declares another local variablea bool called userExited that keeps track of whether the user has chosen to exit. This variable controls a while loop (lines 84110) that allows the user to execute an unlimited number of transactions before choosing to exit. Within this loop, line 87 displays the main menu and obtains the user's menu selection by calling ATM utility method DisplayMainMenu (declared in lines 114123). This method displays the main menu by invoking methods of the ATM's screen and returns a menu selection obtained from the user through the ATM's keypad. Line 87 stores the user's selection, returned by DisplayMainMenu, in local variable mainMenuSelection.
After obtaining a main menu selection, method PerformTransactions uses a switch statement (lines 90109) to respond to the selection appropriately. If mainMenuSelection is equal to the underlying value of any of the three enum members representing transaction types (i.e., if the user chose to perform a transaction), lines 9798 call utility method CreateTransaction (declared in lines 126149) to return a newly instantiated object of the type that corresponds to the selected transaction. Variable currentTransaction is assigned the reference returned by method CreateTransaction, then line 99 invokes method Execute of this transaction to execute it. We discuss TRansaction method Execute and the three TRansaction derived classes shortly. Note that we assign to the TRansaction variable currentTransaction an object of one of the three transaction derived classes so that we can execute transactions. For example, if the user chooses to perform a balance inquiry, ( MenuOption ) mainMenuSelection (line 90) matches the case label MenuOption.BALANCE_INQUIRY, and CreateTransaction returns a BalanceInquiry object (lines 9798). Thus, currentTransaction refers to a BalanceInquiry and invoking currentTransaction.Execute() (line 99) results in BalanceInquiry's version of Execute being called polymorphically.
Creating Transactions
Method CreateTransaction (lines 126149) uses a switch statement (lines 131146) to instantiate a new transaction derived class object of the type indicated by the parameter type. Recall that method PerformTransactions passes mainMenuSelection to method CreateTransaction only when mainMenuSelection contains a value corresponding to one of the three transaction types. So parameter type (line 126) receives one of the values MenuOption.BALANCE_INQUIRY, MenuOption.WITHDRAWAL or MenuOption.DEPOSIT. Each case in the switch statement instantiates a new object by calling the appropriate TRansaction derived class constructor. Note that each constructor has a unique parameter list, based on the specific data required to initialize the derived class object. A BalanceInquiry (lines 135136) requires only the account number of the current user and references to the ATM's screen and the bankDatabase. In addition to these parameters, a Withdrawal (lines 139140) requires references to the ATM's keypad and cashDispenser, and a Deposit (lines 143144) requires references to the ATM's keypad and depositSlot. We discuss the transaction classes in detail in Sections J.9J.12.
After executing a transaction (line 99 in method PerformTransactions), userExited remains false, and the while loop in lines 84110 repeats, returning the user to the main menu. However, if a user does not perform a transaction and instead selects the main menu option to exit, line 103 sets userExited to TRue, causing the condition in line 84 of the while loop (!userExited) to become false. This while is the final statement of method PerformTransactions, so control returns to line 47 of the calling method Run. If the user enters an invalid main menu selection (i.e., not an integer in the range 14), lines 106107 display an appropriate error message, userExited remains false (as set in line 81) and the user returns to the main menu to try again.
When method PerformTransactions returns control to method Run, the user has chosen to exit the system, so lines 4849 reset the ATM's attributes userAuthenticated and currentAccountNumber to false and 0, respectively, to prepare for the next ATM user. Line 50 displays a goodbye message to the current user before the ATM welcomes the next user.