E. ATM Case Study Code

E.1 ATM Case Study Implementation

This appendix contains the complete working implementation of the ATM system that we designed in the Software Engineering Case Study sections found at the ends of Chapters 17, 9 and 13 . The implementation comprises 877 lines of C++ code. We consider the classes in the order in which we identified them in Section 3.11:

•     ATM

•     Screen

•     Keypad

•     CashDispenser

•     DepositSlot

•     Account

•     BankDatabase

•     Transaction

•     BalanceInquiry

•     Withdrawal

•     Deposit

We apply the guidelines discussed in Section 9.11 and Section 13.10 to code these classes based on how we modeled them in the UML class diagrams of Fig. 13.28 and Fig. 13.29. To develop the definitions of classes’ member functions, we refer to the activity diagrams presented in Section 5.10 and the communication and sequence diagrams presented in Section 7.12. Note that our ATM design does not specify all the program logic and may not specify all the attributes and operations required to complete the ATM implementation. This is a normal part of the object-oriented design process. As we implement the system, we complete the program logic and add attributes and behaviors as necessary to construct the ATM system specified by the requirements specification in Section 2.7.

We conclude the discussion by presenting a C++ program (ATMCaseStudy.cpp) that starts the ATM and puts the other classes in the system in use. Recall that we are developing a first version of the ATM system that runs on a personal computer and uses the computer’s keyboard and monitor to approximate the ATM’s keypad and screen. We also only simulate the actions of the ATM’s cash dispenser and deposit slot. We attempt to implement the system, however, so that real hardware versions of these devices could be integrated without significant changes in the code.

E.2 Class ATM

Class ATM (Figs. E.1E.2) represents the ATM as a whole. Figure E.1 contains the ATM class definition, enclosed in #ifndef, #define and #endif preprocessor directives to ensure that this definition gets included only once in a program. We discuss lines 6–11 shortly. Lines 16–17 contain the function prototypes for the class’s public member functions. The class diagram of Fig. 13.29 does not list any operations for class ATM, but we now declare a public member function run (line 17) in class ATM that allows an external client of the class (i.e., ATMCaseStudy.cpp) to tell the ATM to run. We also include a function prototype for a default constructor (line 16), which we discuss shortly.

Fig. E.1 Definition of class ATM, which represents the ATM.

 1   // ATM.h
 2   // ATM class definition. Represents an automated teller machine.
 3   #ifndef ATM_H
 4   #define ATM_H
 5
 6   #include "Screen.h" // Screen class definition
 7   #include "Keypad.h" // Keypad class definition
 8   #include "CashDispenser.h" // CashDispenser class definition
 9   #include "DepositSlot.h" // DepositSlot class definition
10   #include "BankDatabase.h" // BankDatabase class definition
11   class Transaction; // forward declaration of class Transaction
12
13   class ATM
14   {
15   public:
16      ATM(); // constructor initializes data members
17      void run(); // start the ATM
18   private:
19      bool userAuthenticated; // whether user is authenticated
20      int currentAccountNumber; // current user's account number
21      Screen screen; // ATM's screen
22      Keypad keypad; // ATM's keypad
23      CashDispenser cashDispenser; // ATM's cash dispenser
24      DepositSlot depositSlot; // ATM's deposit slot
25      BankDatabase bankDatabase; // account information database
26
27      // private utility functions
28      void authenticateUser(); // attempts to authenticate user
29      void performTransactions(); // performs transactions
30      int displayMainMenu() const; // displays main menu
31
32      // return object of specified Transaction derived class
33      Transaction *createTransaction( int );
34   }; // end class ATM
35
36   #endif // ATM_H

Lines 19–25 of Fig. E.1 implement the class’s attributes as private data members. We determine all but one of these attributes from the UML class diagrams of Fig. 13.28 and Fig. 13.29. Note that we implement the UML Boolean attribute userAuthenticated in Fig. 13.29 as a bool data member in C++ (line 19). Line 20 declares a data member not found in our UML design—an int data member currentAccountNumber that keeps track of the account number of the current authenticated user. We’ll soon see how the class uses this data member.

Lines 21–24 create objects to represent the parts of the ATM. Recall from the class diagram of Fig. 13.28 that class ATM has composition relationships with classes Screen, Keypad, CashDispenser and DepositSlot, so class ATM is responsible for their creation. Line 25 creates a BankDatabase, with which the ATM interacts to access and manipulate bank account information. [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.] Note that lines 6–10 #include the class definitions of Screen, Keypad, CashDispenser, DepositSlot and BankDatabase so that the ATM can store objects of these classes.

Lines 28–30 and 33 contain function prototypes for private utility functions that the class uses to perform its tasks. We’ll see how these functions serve the class shortly. Note that member function createTransaction (line 33) returns a Transaction pointer. To include the class name Transaction in this file, we must at least include a forward declaration of class Transaction (line 11). Recall that a forward declaration tells the compiler that a class exists, but that the class is defined elsewhere. A forward declaration is sufficient here, as we are using a Transaction pointer as a return type—if we were creating or returning an actual Transaction object, we would need to #include the full Transaction header file.

ATM Class Member-Function Definitions

Figure E.2 contains the member-function definitions for class ATM. Lines 3–7 #include the header files required by the implementation file ATM.cpp. Note that including the ATM header file allows the compiler to ensure that the class’s member functions are defined correctly. This also allows the member functions to use the class’s data members.

Fig. E.2 ATM class member-function definitions.

 1   // ATM.cpp
 2   // Member-function definitions for class ATM.
 3   #include "ATM.h" // ATM class definition
 4   #include "Transaction.h" // Transaction class definition
 5   #include "BalanceInquiry.h" // BalanceInquiry class definition
 6   #include "Withdrawal.h" // Withdrawal class definition
 7   #include "Deposit.h" // Deposit class definition
 8
 9   // enumeration constants represent main menu options
10   enum MenuOption { BALANCE_INQUIRY = 1, WITHDRAWAL, DEPOSIT, EXIT };
11
12   // ATM default constructor initializes data members
13   ATM::ATM()
14      : userAuthenticated ( false ), // user is not authenticated to start
15        currentAccountNumber( 0 ) // no current account number to start
16   {
17      // empty body
18   } // end ATM default constructor
19
20   // start ATM
21   void ATM::run()
22   {
23      // welcome and authenticate user; perform transactions
24      while ( true )
25      {
26         // loop while user is not yet authenticated
27         while ( !userAuthenticated )
28         {
29            screen.displayMessageLine( " Welcome!" );
30            authenticateUser(); // authenticate user
31         } // end while
32
33         performTransactions(); // user is now authenticated
34         userAuthenticated = false; // reset before next ATM session
35         currentAccountNumber = 0; // reset before next ATM session
36         screen.displayMessageLine( " Thank you! Goodbye!" );
37      } // end while
38   } // end function run
39
40   // attempt to authenticate user against database
41   void ATM::authenticateUser()
42   {
43      screen.displayMessage( " Please enter your account number: " );
44      int accountNumber = keypad.getInput(); // input account number
45      screen.displayMessage( " Enter your PIN: " ); // prompt for PIN
46      int pin = keypad.getInput(); // input PIN
47
48      // set userAuthenticated to bool value returned by database
49      userAuthenticated =
50         bankDatabase.authenticateUser( accountNumber, pin );
51
52      // check whether authentication succeeded
53      if ( userAuthenticated )
54      {
55         currentAccountNumber = accountNumber; // save user's account #
56      } // end if
57      else
58         screen.displayMessageLine(
59            "Invalid account number or PIN. Please try again." );
60   } // end function authenticateUser
61
62   // display the main menu and perform transactions
63   void ATM::performTransactions()
64   {
65      // local pointer to store transaction currently being processed
66      Transaction *currentTransactionPtr;
67
68      bool userExited = false; // user has not chosen to exit
69
70      // loop while user has not chosen option to exit system
71      while ( !userExited )
72      {
73         // show main menu and get user selection
74         int mainMenuSelection = displayMainMenu();
75
76         // decide how to proceed based on user's menu selection
77         switch ( mainMenuSelection )
78         {
79            // user chose to perform one of three transaction types
80            case BALANCE_INQUIRY:
81            case WITHDRAWAL:
82            case DEPOSIT:
83               // initialize as new object of chosen type
84               currentTransactionPtr =
85                  createTransaction( mainMenuSelection );
86
87               currentTransactionPtr->execute(); // execute transaction
88
89               // free the space for the dynamically allocated Transaction
90               delete currentTransactionPtr;
91
92               break;
93            case EXIT: // user chose to terminate session
94               screen.displayMessageLine( " Exiting the system..." );
95               userExited = true; // this ATM session should end
96               break;
97            default: // user did not enter an integer from 1-4
98               screen.displayMessageLine(
99                  " You did not enter a valid selection. Try again." );
100              break;
101        } // end switch
102     } // end while
103  } // end function performTransactions
104
105  // display the main menu and return an input selection
106  int ATM::displayMainMenu() const
107  {
108     screen.displayMessageLine( " Main menu:" );
109     screen.displayMessageLine( "1 - View my balance" );
110     screen.displayMessageLine( "2 - Withdraw cash" );
111     screen.displayMessageLine( "3 - Deposit funds" );
112     screen.displayMessageLine( "4 - Exit " );
113     screen.displayMessage( "Enter a choice: " );
114     return keypad.getInput(); // return user's selection
115  } // end function displayMainMenu
116
117  // return object of specified Transaction derived class
118  Transaction *ATM::createTransaction( int type )
119  {
120     Transaction *tempPtr; // temporary Transaction pointer
121
122     // determine which type of Transaction to create
123     switch ( type )
124     {
125        case BALANCE_INQUIRY: // create new BalanceInquiry transaction
126           tempPtr = new BalanceInquiry(
127              currentAccountNumber, screen, bankDatabase );
128           break;
129        case WITHDRAWAL: // create new Withdrawal transaction
130           tempPtr = new Withdrawal( currentAccountNumber, screen,
131              bankDatabase, keypad, cashDispenser );
132           break;
133        case DEPOSIT: // create new Deposit transaction
134           tempPtr = new Deposit( currentAccountNumber, screen,
135              bankDatabase, keypad, depositSlot );
136           break;
137     } // end switch
138
139     return tempPtr; // return the newly created object
140  } // end function createTransaction

Line 10 declares an enum named MenuOption that contains constants corresponding to the four options in the ATM’s main menu (i.e., balance inquiry, withdrawal, deposit and exit). Note that setting BALANCE_INQUIRY to 1 causes the subsequent enumeration constants to be assigned the values 2, 3 and 4, as enumeration constant values increment by 1.

Lines 13–18 define class ATM’s constructor, which initializes the class’s data members. When an ATM object is first created, no user is authenticated, so line 14 uses a member initializer to set userAuthenticated to false. Likewise, line 15 initializes currentAccountNumber to 0 because there is no current user yet.

ATM member function run (lines 21–38) uses an infinite loop (lines 24–37) 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 chooses to exit, the ATM resets itself, displays a goodbye message to the user and restarts the process. 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 has the option to exit the system, but does not have the ability to turn off the ATM completely.

Inside member function run’s infinite loop, lines 27–31 cause the ATM to repeatedly welcome and attempt to authenticate the user as long as the user has not been authenticated (i.e., !userAuthenticated is true). Line 29 invokes member function displayMessageLine of the ATM’s screen to display a welcome message. Like Screen member function displayMessage designed in the case study, member function displayMessageLine (declared in line 13 of Fig. E.3 and defined in lines 20–23 of Fig. E.4) displays a message to the user, but this member function also outputs a newline after displaying the message. We have added this member function during implementation to give class Screen’s clients more control over the placement of displayed messages. Line 30 of Fig. E.2 invokes class ATM’s private utility function authenticateUser (lines 41–60) to attempt to authenticate the user.

Fig. E.3 Screen class definition.

 1   // Screen.h
 2   // Screen class definition. Represents the screen of the ATM.
 3   #ifndef SCREEN_H
 4   #define SCREEN_H
 5
 6   #include <string>
 7   using std::string;
 8
 9   class Screen
10   {
11   public:
12      void displayMessage( string ) const; // output a message
13      void displayMessageLine( string ) const; // output message with newline
14      void displayDollarAmount( double ) const; // output a dollar amount
15   }; // end class Screen
16
17   #endif // SCREEN_H

Fig. E.4 Screen class member-function definitions.

 1   // Screen.cpp
 2   // Member-function definitions for class Screen.
 3   #include <iostream>
 4   using std::cout;
 5   using std::endl;
 6   using std::fixed;
 7
 8   #include <iomanip>
 9   using std::setprecision;
10
11   #include "Screen.h" // Screen class definition
12
13   // output a message without a newline
14   void Screen::displayMessage( string message ) const
15   {
16      cout << message;
17   } // end function displayMessage
18
19   // output a message with a newline
20   void Screen::displayMessageLine( string message ) const
21   {
22      cout << message << endl;
23   } // end function displayMessageLine
24
25   // output a dollar amount
26   void Screen::displayDollarAmount( double amount ) const
27   {
28      cout << fixed << setprecision( 2 ) << "$" << amount;
29   } // end function displayDollarAmount

We refer to the requirements specification to determine the steps necessary to authenticate the user before allowing transactions to occur. Line 43 of member function authenticateUser invokes member function displayMessage of the ATM’s screen to prompt the user to enter an account number. Line 44 invokes member function getInput of the ATM’s keypad to obtain the user’s input, then stores the integer value entered by the user in a local variable accountNumber. Member function authenticateUser next prompts the user to enter a PIN (line 45), and stores the PIN input by the user in a local variable pin (line 46). Next, lines 49–50 attempt to authenticate the user by passing the accountNumber and pin entered by the user to the bankDatabase’s authenticateUser member function. Class ATM sets its userAuthenticated data member to the bool value returned by this function—userAuthenticated becomes true if authentication succeeds (i.e., accountNumber and pin match those of an existing Account in bankDatabase) and remains false otherwise. If userAuthenticated is true, line 55 saves the account number entered by the user (i.e., accountNumber) in the ATM data member currentAccountNumber. The other member functions of class ATM use this variable whenever an ATM session requires access to the user’s account number. If userAuthenticated is false, lines 58–59 use the screen’s displayMessageLine member function to indicate that an invalid account number and/or PIN was entered and the user must try again. Note that we set currentAccountNumber only after authenticating the user’s account number and the associated PIN—if the database could not authenticate the user, currentAccountNumber remains 0.

After member function run attempts to authenticate the user (line 30), if userAuthenticated is still false, the while loop in lines 27–31 executes again. If userAuthenticated is now true, the loop terminates and control continues with line 33, which calls class ATM’s utility function performTransactions.

Member function performTransactions (lines 63–103) carries out an ATM session for an authenticated user. Line 66 declares a local Transaction pointer, which we aim at a BalanceInquiry, Withdrawal or Deposit object representing the ATM transaction currently being processed. Note that we use a Transaction pointer here to allow us to take advantage of polymorphism. Also note that we use the role name included in the class diagram of Fig. 3.20currentTransaction—in naming this pointer. As per our pointernaming convention, we append “Ptr” to the role name to form the variable name currentTransactionPtr. Line 68 declares another local variable—a bool called userExited that keeps track of whether the user has chosen to exit. This variable controls a while loop (lines 71–102) that allows the user to execute an unlimited number of transactions before choosing to exit. Within this loop, line 74 displays the main menu and obtains the user’s menu selection by calling an ATM utility function displayMainMenu (defined in lines 106–115). This member function displays the main menu by invoking member functions of the ATM’s screen and returns a menu selection obtained from the user through the ATM’s keypad. Note that this member function is const because it does not modify the contents of the object. Line 74 stores the user’s selection returned by displayMainMenu in local variable mainMenuSelection.

After obtaining a main menu selection, member function performTransactions uses a switch statement (lines 77–101) to respond to the selection appropriately. If mainMenuSelection is equal to any of the three enumeration constants representing transaction types (i.e., if the user chose to perform a transaction), lines 84–85 call utility function createTransaction (defined in lines 118–140) to return a pointer to a newly instantiated object of the type that corresponds to the selected transaction. Pointer currentTransactionPtr is assigned the pointer returned by createTransaction. Line 87 then uses currentTransactionPtr to invoke the new object’s execute member function to execute the transaction. We’ll discuss Transaction member function execute and the three Transaction derived classes shortly. Finally, when the Transaction derived class object is no longer needed, line 90 releases the memory dynamically allocated for it.

Note that we aim the Transaction pointer currentTransactionPtr at an object of one of the three Transaction derived classes so that we can execute transactions polymorphically. For example, if the user chooses to perform a balance inquiry, mainMenuSelection equals BALANCE_INQUIRY, leading createTransaction to return a pointer to a BalanceInquiry object. Thus, currentTransactionPtr points to a BalanceInquiry, and invoking currentTransactionPtr->execute() results in BalanceInquiry’s version of execute being called.

Member function createTransaction (lines 118–140) uses a switch statement (lines 123–137) to instantiate a new Transaction derived class object of the type indicated by the parameter type. Recall that member function performTransactions passes mainMenuSelection to this member function only when mainMenuSelection contains a value corresponding to one of the three transaction types. Therefore type equals either BALANCE_INQUIRY, WITHDRAWAL or DEPOSIT. Each case in the switch statement aims the temporary pointer tempPtr at a newly created object of the appropriate Transaction derived class. Note that each constructor has a unique parameter list, based on the specific data required to initialize the derived class object. A BalanceInquiry 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 requires references to the ATM’s keypad and cashDispenser, and a Deposit requires references to the ATM’s keypad and depositSlot. Note that, as you’ll soon see, the BalanceInquiry, Withdrawal and Deposit constructors each specify reference parameters to receive the objects representing the required parts of the ATM. Thus, when member function createTransaction passes objects in the ATM (e.g., screen and keypad) to the initializer for each newly created Transaction derived class object, the new object actually receives references to the ATM’s composite objects. We discuss the transaction classes in more detail in Sections E.9E.12.

After executing a transaction (line 87 in performTransactions), userExited remains false and the while loop in lines 71–102 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 95 sets userExited to true, causing the condition of the while loop (!userExited) to become false. This while is the final statement of member function performTransactions, so control returns to the calling function run. If the user enters an invalid main menu selection (i.e., not an integer from 1–4), lines 98–99 display an appropriate error message, userExited remains false and the user returns to the main menu to try again.

When performTransactions returns control to member function run, the user has chosen to exit the system, so lines 34–35 reset the ATM’s data members userAuthenticated and currentAccountNumber to prepare for the next ATM user. Line 36 displays a goodbye message before the ATM starts over and welcomes the next user.

E.3 Class Screen

Class Screen (Figs. E.3E.4) represents the screen of the ATM and encapsulates all aspects of displaying output to the user. Class Screen approximates a real ATM’s screen with a computer monitor and outputs text messages using cout and the stream insertion operator (<<). In this case study, we designed class Screen to have one operation—displayMessage. For greater flexibility in displaying messages to the Screen, we now declare three Screen member functions—displayMessage, displayMessageLine and displayDollarAmount. The prototypes for these member functions appear in lines 12–14 of Fig. E.3.

Screen Class Member-Function Definitions

Figure E.4 contains the member-function definitions for class Screen. Line 11 #includes the Screen class definition. Member function displayMessage (lines 14–17) takes a string as an argument and prints it to the console using cout and the stream insertion operator (<<). The cursor stays on the same line, making this member function appropriate for displaying prompts to the user. Member function displayMessageLine (lines 20–23) also prints a string, but outputs a newline to move the cursor to the next line. Finally, member function displayDollarAmount (lines 26–29) outputs a properly formatted dollar amount (e.g., $123.45). Line 28 uses stream manipulators fixed and setprecision to output a value formatted with two decimal places. See Chapter 15, Stream Input/Output, for more information about formatting output.

E.4 Class Keypad

Class Keypad (Figs. E.5E.6) represents the keypad of the ATM and is responsible for receiving all user input. Recall that we are simulating this hardware, so we use the computer’s keyboard to approximate the keypad. A computer keyboard contains many keys not found on the ATM’s keypad. However, we assume that the user presses only the keys on the computer keyboard that also appear on the keypad—the keys numbered 0–9 and the Enter key. Line 9 of Fig. E.5 contains the function prototype for class Keypad’s one member function getInput. This member function is declared const because it does not change the object.

Fig. E.5 Keypad class definition.

 1   // Keypad.h
 2   // Keypad class definition. Represents the keypad of the ATM.
 3   #ifndef KEYPAD_H
 4   #define KEYPAD_H
 5
 6   class Keypad
 7   {
 8   public:
 9      int getInput() const; // return an integer value entered by user
10   }; // end class Keypad
11
12   #endif // KEYPAD_H

Keypad Class Member-Function Definition

In the Keypad implementation file (Fig. E.6), member function getInput (defined in lines 9–14) uses the standard input stream cin and the stream extraction operator (>>) to obtain input from the user. Line 11 declares a local variable to store the user’s input. Line 12 reads input into local variable input, then line 13 returns this value. Recall that getInput obtains all the input used by the ATM. Keypad’s getInput member function simply returns the integer input by the user. If a client of class Keypad requires input that satisfies some particular criteria (i.e., a number corresponding to a valid menu option), the client must perform the appropriate error checking. [Note: Using the standard input stream cin and the stream extraction operator (>>) allows noninteger input to be read from the user. Because the real ATM’s keypad permits only integer input, however, we assume that the user enters an integer and do not attempt to fix problems caused by noninteger input.]

Fig. E.6 Keypad class member-function definition.

 1   // Keypad.cpp
 2   // Member-function definition for class Keypad (the ATM's keypad).
 3   #include <iostream>
 4   using std::cin;
 5
 6   #include "Keypad.h" // Keypad class definition
 7
 8   // return an integer value entered by user
 9   int Keypad::getInput() const
10   {
11      int input; // variable to store the input
12      cin >> input; // we assume that user enters an integer
13      return input; // return the value entered by user
14   } // end function getInput

E.5 Class CashDispenser

Class CashDispenser (Figs. E.7E.8) represents the cash dispenser of the ATM. The class definition (Fig. E.7) contains the function prototype for a default constructor (line 9). Class CashDispenser declares two additional public member functions—dispenseCash (line 12) and isSufficientCashAvailable (line 15). The class trusts that a client (i.e., Withdrawal) calls dispenseCash only after establishing that sufficient cash is available by calling isSufficientCashAvailable. Thus, dispenseCash simply simulates dispensing the requested amount without checking whether sufficient cash is available. Line 17 declares private constant INITIAL_COUNT, which indicates the initial count of bills in the cash dispenser when the ATM starts (i.e., 500). Line 18 implements attribute count (modeled in Fig. 13.29), which keeps track of the number of bills remaining in the CashDispenser at any time.

Fig. E.7 CashDispenser class definition.

 1   // CashDispenser.h
 2   // CashDispenser class definition. Represents the ATM's cash dispenser.
 3   #ifndef CASH_DISPENSER_H
 4   #define CASH_DISPENSER_H
 5
 6   class CashDispenser
 7   {
 8   public:
 9      CashDispenser(); // constructor initializes bill count to 500
10
11      // simulates dispensing of specified amount of cash
12      void dispenseCash( int );
13
14      // indicates whether cash dispenser can dispense desired amount
15      bool isSufficientCashAvailable( int ) const;
16   private:
17      const static int INITIAL_COUNT = 500;
18      int count; // number of $20 bills remaining
19   }; // end class CashDispenser
20
21   #endif // CASH_DISPENSER_H

CashDispenser Class Member-Function Definitions

Figure E.8 contains the definitions of class CashDispenser’s member functions. The constructor (lines 6–9) sets count to the initial count (i.e., 500). Member function dispenseCash (lines 13–17) simulates cash dispensing. If our system were hooked up to a real hardware cash dispenser, this member function would interact with the hardware device to physically dispense cash. Our simulated version of the member function simply decreases the count of bills remaining by the number required to dispense the specified amount (line 16). Note that line 15 calculates the number of $20 bills required to dispense the specified amount. The ATM allows the user to choose only withdrawal amounts that are multiples of $20, so we divide amount by 20 to obtain the number of billsRequired. Also note that it is the responsibility of the client of the class (i.e., Withdrawal) to inform the user that cash has been dispensed—CashDispenser cannot interact directly with Screen.

Fig. E.8 CashDispenser class member-function definitions.

 1   // CashDispenser.cpp
 2   // Member-function definitions for class CashDispenser.
 3   #include "CashDispenser.h" // CashDispenser class definition
 4
 5   // CashDispenser default constructor initializes count to default
 6   CashDispenser::CashDispenser()
 7   {
 8      count = INITIAL_COUNT; // set count attribute to default
 9   } // end CashDispenser default constructor
10
11   // simulates dispensing of specified amount of cash; assumes enough cash
12   // is available (previous call to isSufficientCashAvailable returned true)
13   void CashDispenser::dispenseCash( int amount )
14   {
15      int billsRequired = amount / 20; // number of $20 bills required
16      count -= billsRequired; // update the count of bills
17   } // end function dispenseCash
18
19   // indicates whether cash dispenser can dispense desired amount
20   bool CashDispenser::isSufficientCashAvailable( int amount ) const
21   {
22      int billsRequired = amount / 20; // number of $20 bills required
23
24      if ( count >= billsRequired )
25         return true; // enough bills are available
26      else
27         return false; // not enough bills are available
28   } // end function isSufficientCashAvailable

Member function isSufficientCashAvailable (lines 20–28) has a parameter amount that specifies the amount of cash in question. Lines 24–27 return true if the CashDispenser’s count is greater than or equal to billsRequired (i.e., enough bills are available) and false otherwise (i.e., not enough bills). For example, if a user wishes to withdraw $80 (i.e., billsRequired is 4), but only three bills remain (i.e., count is 3), the member function returns false.

E.6 Class DepositSlot

Class DepositSlot (Figs. E.9E.10) represents the deposit slot of the ATM. Like the version of class CashDispenser presented here, this version of class DepositSlot merely simulates the functionality of a real hardware deposit slot. DepositSlot has no data members and only one member function—isEnvelopeReceived (declared in line 9 of Fig. E.9 and defined in lines 7–10 of Fig. E.10)—that indicates whether a deposit envelope was received.

Fig. E.9 DepositSlot class definition.

 1   // DepositSlot.h
 2   // DepositSlot class definition. Represents the ATM's deposit slot.
 3   #ifndef DEPOSIT_SLOT_H
 4   #define DEPOSIT_SLOT_H
 5
 6   class DepositSlot
 7   {
 8   public:
 9      bool isEnvelopeReceived() const// tells whether envelope was received
10   }; // end class DepositSlot
11
12   #endif // DEPOSIT_SLOT_H

Fig. E.10 DepositSlot class member-function definition.

 1   // DepositSlot.cpp
 2   // Member-function definition for class DepositSlot.
 3   #include "DepositSlot.h" // DepositSlot class definiton
 4
 5   // indicates whether envelope was received (always returns true,
 6   // because this is only a software simulation of a real deposit slot)
 7   bool DepositSlot::isEnvelopeReceived() const
 8   {
 9      return true; // deposit envelope was received
10   } // end function isEnvelopeReceived

Recall from the requirements specification that the ATM allows the user up to two minutes to insert an envelope. The current version of member function isEnvelopeReceived simply returns true immediately (line 9 of Fig. E.10), because this is only a software simulation, and we assume that the user has inserted an envelope within the required time frame. If an actual hardware deposit slot were connected to our system, member function isEnvelopeReceived might be implemented to wait for a maximum of two minutes to receive a signal from the hardware deposit slot indicating that the user has indeed inserted a deposit envelope. If isEnvelopeReceived were to receive such a signal within two minutes, the member function would return true. If two minutes elapsed and the member function still had not received a signal, then the member function would return false.

E.7 Class Account

Class Account (Figs. E.11E.12) represents a bank account. Lines 9–15 in the class definition (Fig. E.11) contain function prototypes for the class’s constructor and six member functions, which we discuss shortly. Each Account has four attributes (modeled in Fig. 13.29)—accountNumber, pin, availableBalance and totalBalance. Lines 17–20 implement these attributes as private data members. Data member availableBalance represents the amount of funds available for withdrawal. Data member totalBalance represents the amount of funds available, plus the amount of deposited funds still pending confirmation or clearance.

Fig. E.11 Account class definition.

 1   // Account.h
 2   // Account class definition. Represents a bank account.
 3   #ifndef ACCOUNT_H
 4   #define ACCOUNT_H
 5
 6   class Account
 7   {
 8   public:
 9      Account( int, int, double, double ); // constructor sets attributes
10      bool validatePIN( int ) const; // is user-specified PIN correct?
11      double getAvailableBalance() const; // returns available balance
12      double getTotalBalance() const; // returns total balance
13      void credit( double ); // adds an amount to the Account balance
14      void debit( double ); // subtracts an amount from the Account balance
15      int getAccountNumber() const; // returns account number
16   private:
17      int accountNumber; // account number
18      int pin; // PIN for authentication
19      double availableBalance; // funds available for withdrawal
20      double totalBalance; // funds available + funds waiting to clear
21   }; // end class Account
22
23   #endif // ACCOUNT_H

Account Class Member-Function Definitions

Figure E.12 presents the definitions of class Account’s member functions. The class’s con-structor (lines 6–14) takes an account number, the PIN established for the account, the initial available balance and the initial total balance as arguments. Lines 8–11 assign these values to the class’s data members using member initializers.

Fig. E.12 Account class member-function definitions.

 1   // Account.cpp
 2   // Member-function definitions for class Account.
 3   #include "Account.h" // Account class definition
 4
 5   // Account constructor initializes attributes
 6   Account::Account( int theAccountNumber, int thePIN,
 7      double theAvailableBalance, double theTotalBalance )
 8      : accountNumber( theAccountNumber ),
 9        pin( thePIN ),
10        availableBalance( theAvailableBalance ),
11        totalBalance( theTotalBalance )
12   {
13      // empty body
14   } // end Account constructor
15
16   // determines whether a user-specified PIN matches PIN in Account
17   bool Account::validatePIN( int userPIN ) const
18   {
19      if ( userPIN == pin )
20         return true;
21      else
22         return false;
23   } // end function validatePIN
24
25   // returns available balance
26   double Account::getAvailableBalance() const
27   {
28      return availableBalance;
29   } // end function getAvailableBalance
30
31   // returns the total balance
32   double Account::getTotalBalance() const
33   {
34      return totalBalance;
35   } // end function getTotalBalance
36
37   // credits an amount to the account
38   void Account::credit( double  amount )
39   {
40      totalBalance += amount; // add to total balance
41   } // end function credit
42
43   // debits an amount from the account
44   void Account::debit( double amount )
45   {
46      availableBalance -= amount; // subtract from available balance
47      totalBalance -= amount; // subtract from total balance
48   } // end function debit
49
50   // returns account number
51   int Account::getAccountNumber() const
52   {
53      return accountNumber;
54   } // end function getAccountNumber

Member function validatePIN (lines 17–23) determines whether a user-specified PIN (i.e., parameter userPIN) matches the PIN associated with the account (i.e., data member pin). Recall that we modeled this member function’s parameter userPIN in the UML class diagram of Fig. 6.35. If the two PINs match, the member function returns true (line 20); otherwise, it returns false (line 22).

Member functions getAvailableBalance (lines 26–29) and getTotalBalance (lines 32–35) are get functions that return the values of double data members availableBalance and totalBalance, respectively.

Member function credit (lines 38–41) adds an amount of money (i.e., parameter amount) to an Account as part of a deposit transaction. Note that this member function adds the amount only to data member totalBalance (line 40). The money credited to an account during a deposit does not become available immediately, so we modify only the total balance. We assume that the bank updates the available balance appropriately at a later time. Our implementation of class Account includes only member functions required for carrying out ATM transactions. Therefore, we omit the member functions that some other bank system would invoke to add to data member availableBalance (to confirm a deposit) or subtract from data member totalBalance (to reject a deposit).

Member function debit (lines 44–48) subtracts an amount of money (i.e., parameter amount) from an Account as part of a withdrawal transaction. This member function subtracts the amount from both data member availableBalance (line 46) and data member totalBalance (line 47), because a withdrawal affects both measures of an account balance.

Member function getAccountNumber (lines 51–54) provides access to an Account’s accountNumber. We include this member function in our implementation so that a client of the class (i.e., BankDatabase) can identify a particular Account. For example, BankDatabase contains many Account objects, and it can invoke this member function on each of its Account objects to locate the one with a specific account number.

E.8 Class BankDatabase

Class BankDatabase (Figs. E.13E.14) models the bank’s database with which the ATM interacts to access and modify a user’s account information. The class definition (Fig. E.13) declares function prototypes for the class’s constructor and several member functions. We discuss these momentarily. The class definition also declares the BankDatabase’s data members. We determine one data member for class BankDatabase based on its composition relationship with class Account. Recall from Fig. 13.28 that a BankDatabase is composed of zero or more objects of class Account. Line 24 of Fig. E.13 implements data member accounts—a vector of Account objects—to implement this composition relationship. Lines 6–7 allow us to use vector in this file. Line 27 contains the function prototype for a private utility function getAccount that allows the member functions of the class to obtain a pointer to a specific Account in the accounts vector.

Fig. E.13 BankDatabase class definition.

 1   // BankDatabase.h
 2   // BankDatabase class definition. Represents the bank's database.
 3   #ifndef BANK_DATABASE_H
 4   #define BANK_DATABASE_H
 5
 6   #include <vector> // class uses vector to store Account objects
 7   using std::vector;
 8
 9   #include "Account.h" // Account class definition
10
11   class BankDatabase
12   {
13   public:
14      BankDatabase(); // constructor initializes accounts
15
16      // determine whether account number and PIN match those of an Account
17      bool authenticateUser( int, int ); // returns true if Account authentic
18
19      double getAvailableBalance( int ); // get an available balance
20      double getTotalBalance( int ); // get an Account's total balance
21      void credit( int, double ); // add amount to Account balance
22      void debit( int, double ); // subtract amount from Account balance
23   private:
24      vector< Account > accounts; // vector of the bank's Accounts
25
26      // private utility function
27      Account * getAccount( int ); // get pointer to Account object
28   }; // end class BankDatabase
29
30   #endif // BANK_DATABASE_H

BankDatabase Class Member-Function Definitions

Figure E.14 contains the member-function definitions for class BankDatabase. We implement the class with a default constructor (lines 6–15) that adds Account objects to data member accounts. For the sake of testing the system, we create two new Account objects with test data (lines 9–10), then add them to the end of the vector (lines 13–14). Note that the Account constructor has four parameters—the account number, the PIN assigned to the account, the initial available balance and the initial total balance.

Fig. E.14 BankDatabase class member-function definitions.

 1   // BankDatabase.cpp
 2   // Member-function definitions for class BankDatabase.
 3   #include "BankDatabase.h" // BankDatabase class definition
 4
 5   // BankDatabase default constructor initializes accounts
 6   BankDatabase::BankDatabase()
 7   {
 8      // create two Account objects for testing
 9      Account account1( 12345543211000.01200.0 );
10      Account account2( 9876556789200.0200.0 );
11
12      // add the Account objects to the vector accounts
13      accounts.push_back( account1 ); // add account1 to end of vector
14      accounts.push_back( account2 ); // add account2 to end of vector
15   } // end BankDatabase default constructor
16
17   // retrieve Account object containing specified account number
18   Account * BankDatabase::getAccount( int accountNumber )
19   {
20      // loop through accounts searching for matching account number
21      for ( size_t i = 0; i < accounts.size(); i++ )
22      {
23         // return current account if match found
24         if ( accounts[ i ].getAccountNumber() == accountNumber )
25            return &accounts[ i ];
26      } // end for
27
28      return NULL; // if no matching account was found, return NULL
29   } // end function getAccount
30
31   // determine whether user-specified account number and PIN match
32   // those of an account in the database
33   bool BankDatabase::authenticateUser( int userAccountNumber,
34      int userPIN )
35   {
36      // attempt to retrieve the account with the account number
37      Account * const userAccountPtr = getAccount( userAccountNumber );
38
39      // if account exists, return result of Account function validatePIN
40      if ( userAccountPtr != NULL )
41         return userAccountPtr->validatePIN( userPIN );
42      else
43         return false; // account number not found, so return false
44   } // end function authenticateUser
45
46   // return available balance of Account with specified account number
47   double BankDatabase::getAvailableBalance( int userAccountNumber )
48   {
49      Account * const userAccountPtr = getAccount( userAccountNumber );
50      return userAccountPtr->getAvailableBalance();
51   } // end function getAvailableBalance
52
53   // return total balance of Account with specified account number
54   double BankDatabase::getTotalBalance( int userAccountNumber )
55   {
56      Account * const userAccountPtr = getAccount( userAccountNumber );
57      return userAccountPtr->getTotalBalance();
58   } // end function getTotalBalance
59
60   // credit an amount to Account with specified account number
61   void BankDatabase::credit( int userAccountNumber, double amount )
62   {
63      Account * const userAccountPtr = getAccount( userAccountNumber );
64      userAccountPtr->credit( amount );
65   } // end function credit
66
67   // debit an amount from Account with specified account number
68   void BankDatabase::debit( int userAccountNumber, double amount )
69   {
70      Account * const  userAccountPtr = getAccount( userAccountNumber );
71      userAccountPtr->debit( amount );
72   } // end function debit

Recall that class BankDatabase serves as an intermediary between class ATM and the actual Account objects that contain users’ account information. Thus, the member functions of class BankDatabase do nothing more than invoke the corresponding member functions of the Account object belonging to the current ATM user.

We include private utility function getAccount (lines 18–29) to allow the BankDatabase to obtain a pointer to a particular Account within vector accounts. To locate the user’s Account, the BankDatabase compares the value returned by member function getAccountNumber for each element of accounts to a specified account number until it finds a match. Lines 21–26 traverse the accounts vector. If the account number of the current Account (i.e., accounts[ i ]) equals the value of parameter accountNumber, the member function immediately returns the address of the current Account (i.e., a pointer to the current Account). If no account has the given account number, then line 28 returns NULL. Note that this member function must return a pointer, as opposed to a reference, because there is the possibility that the return value could be NULL—a reference cannot be NULL, but a pointer can.

Note that vector function size (invoked in the loop-continuation condition in line 21) returns the number of elements in a vector as a value of type size_t (which is usually unsigned int). As a result, we declare the control variable i to be of type size_t, too. On some compilers, declaring i as an int would cause the compiler to issue a warning message, because the loop-continuation condition would compare a signed value (i.e., an int) and an unsigned value (i.e., a value of type size_t).

Member function authenticateUser (lines 33–44) proves or disproves the an ATM user’s identity. This function takes a user-specified account number and user-specified PIN as arguments and indicates whether they match the account number and PIN of an Account in the database. Line 37 calls utility function getAccount, which returns either a pointer to an Account with userAccountNumber as its account number or NULL to indicate that userAccountNumber is invalid. We declare userAccountPtr to be a const pointer because, once the member function aims this pointer at the user’s Account, the pointer should not change. If getAccount returns a pointer to an Account object, line 41 returns the bool value returned by that object’s validatePIN member function. Note that BankDatabase’s authenticateUser member function does not perform the PIN comparison itself—rather, it forwards userPIN to the Account object’s validatePIN member function to do so. The value returned by Account member function validatePIN indicates whether the user-specified PIN matches the PIN of the user’s Account, so member function authenticateUser simply returns this value to the client of the class (i.e., ATM).

BankDatabase trusts the ATM to invoke member function authenticateUser and receive a return value of true before allowing the user to perform transactions. BankDatabase also trusts that each Transaction object created by the ATM contains the valid account number of the current authenticated user and that this is the account number passed to the remaining BankDatabase member functions as argument userAccountNumber. Member functions getAvailableBalance (lines 47–51), getTotalBalance (lines 54–58), credit (lines 61–65) and debit (lines 68–72) therefore simply retrieve a pointer to the user’s Account object with utility function getAccount, then use this pointer to invoke the appropriate Account member function on the user’s Account object. We know that the calls to getAccount within these member functions will never return NULL, because userAccountNumber must refer to an existing Account. Note that getAvailableBalance and getTotalBalance return the values returned by the corresponding Account member functions. Also note that credit and debit simply redirect parameter amount to the Account member functions they invoke.

E.9 Class Transaction

Class Transaction (Figs. E.15E.16) is an abstract base class that represents the notion of an ATM transaction. It contains the common features of derived classes BalanceInquiry, Withdrawal and Deposit. Figure E.15 expands upon the Transaction header file first developed in Section 13.10. Lines 13, 17–19 and 22 contain function prototypes for the class’s constructor and four member functions, which we discuss shortly. Line 15 defines a virtual destructor with an empty body—this makes all derived-class destructors virtual (even those defined implicitly by the compiler) and ensures that dynamically allocated derived-class objects get destroyed properly when they are deleted via a base-class pointer. Lines 24–26 declare the class’s private data members. Recall from the class diagram of Fig. 13.29 that class Transaction contains an attribute accountNumber (implemented in line 24) that indicates the account involved in the Transaction. We derive data members screen (line 25) and bankDatabase (line 26) from class Transaction’s associations modeled in Fig. 13.28—all transactions require access to the ATM’s screen and the bank’s database, so we include references to a Screen and a BankDatabase as data members of class Transaction. As you’ll soon see, Transaction’s constructor initializes these references. Note that the forward declarations in lines 6–7 signify that the header file contains references to objects of classes Screen and BankDatabase, but that the definitions of these classes lie outside the header file.

Fig. E.15 Transaction class definition.

 1   // Transaction.h
 2   // Transaction abstract base class definition.
 3   #ifndef TRANSACTION_H
 4   #define TRANSACTION_H
 5
 6   class Screen; // forward declaration of class Screen
 7   class BankDatabase; // forward declaration of class BankDatabase
 8

 9   class Transaction
10   {
11   public:
12      // constructor initializes common features of all Transactions
13      Transaction( int, Screen &, BankDatabase & );
14
15      virtual ~Transaction() { } // virtual destructor with empty body
16
17      int getAccountNumber() const; // return account number
18      Screen &getScreen() const; // return reference to screen
19      BankDatabase &getBankDatabase() const; // return reference to database
20
21      // pure virtual function to perform the transaction
22      virtual void execute() = 0; // overridden in derived classes
23   private:
24      int accountNumber; // indicates account involved
25      Screen &screen; // reference to the screen of the ATM
26      BankDatabase &bankDatabase; // reference to the account info database
27   }; // end class Transaction
28
29   #endif // TRANSACTION_H

Class Transaction has a constructor (declared in line 13 of Fig. E.15 and defined in lines 8–15 of Fig. E.16) that takes the current user’s account number and references to the ATM’s screen and the bank’s database as arguments. Because Transaction is an abstract class, this constructor will never be called directly to instantiate Transaction objects. Instead, the constructors of the Transaction derived classes will use base-class initializer syntax to invoke this constructor.

Fig. E.16 Transaction class member-function definitions.

 1   // Transaction.cpp
 2   // Member-function definitions for class Transaction.
 3   #include "Transaction.h" // Transaction class definition
 4   #include "Screen.h" // Screen class definition
 5   #include "BankDatabase.h" // BankDatabase class definition
 6
 7   // constructor initializes common features of all Transactions
 8   Transaction::Transaction( int userAccountNumber, Screen &atmScreen,
 9      BankDatabase &atmBankDatabase )
10      : accountNumber( userAccountNumber ),
11        screen( atmScreen ),
12        bankDatabase( atmBankDatabase )
13   {
14      // empty body
15   } // end Transaction constructor
16
17   // return account number
18   int Transaction::getAccountNumber() const
19   {
20      return accountNumber;
21   } // end function getAccountNumber
22
23   // return reference to screen
24   Screen &Transaction::getScreen() const
25   {
26      return screen;
27   } // end function getScreen
28
29   // return reference to bank database
30   BankDatabase &Transaction::getBankDatabase() const
31   {
32      return bankDatabase;
33   } // end function getBankDatabase

Class Transaction has three public get functions—getAccountNumber (declared in line 17 of Fig. E.15 and defined in lines 18–21 of Fig. E.16), getScreen (declared in line 18 of Fig. E.15 and defined in lines 24–27 of Fig. E.16) and getBankDatabase (declared in line 19 of Fig. E.15 and defined in lines 30–33 of Fig. E.16). Transaction derived classes inherit these member functions from Transaction and use them to gain access to class Transaction’s private data members.

Class Transaction also declares a pure virtual function execute (line 22 of Fig. E.15). It does not make sense to provide an implementation for this member function, because a generic transaction cannot be executed. Thus, we declare this member function to be a pure virtual function and force each Transaction derived class to provide its own concrete implementation that executes that particular type of transaction.

E.10 Class BalanceInquiry

Class BalanceInquiry (Figs. E.17E.18) derives from abstract base class Transaction and represents a balance-inquiry ATM transaction. BalanceInquiry does not have any data members of its own, but it inherits Transaction data members accountNumber, screen and bankDatabase, which are accessible through Transaction’s public get functions. Note that line 6 #includes the definition of base class Transaction. The BalanceInquiry constructor (declared in line 11 of Fig. E.17 and defined in lines 8–13 of Fig. E.18) takes arguments corresponding to the Transaction data members and simply forwards them to Transaction’s constructor, using base-class initializer syntax (line 10 of Fig. E.18). Line 12 of Fig. E.17 contains the function prototype for member function execute, which is required to indicate the intention to override the base class’s pure virtual function of the same name.

Fig. E.17 BalanceInquiry class definition.

 1   // BalanceInquiry.h
 2   // BalanceInquiry class definition. Represents a balance inquiry.
 3   #ifndef BALANCE_INQUIRY_H
 4   #define BALANCE_INQUIRY_H
 5
 6   #include "Transaction.h" // Transaction class definition
 7
 8   class BalanceInquiry : public Transaction
 9   {
10   public:
11      BalanceInquiry( int, Screen &, BankDatabase & ); // constructor
12      virtual void execute(); // perform the transaction
13   }; // end class BalanceInquiry
14
15   #endif // BALANCE_INQUIRY_H

Class BalanceInquiry overrides Transaction’s pure virtual function execute to provide a concrete implementation (lines 16–37 of Fig. E.18) that performs the steps involved in a balance inquiry. Lines 19–20 get references to the bank database and the ATM’s screen by invoking member functions inherited from base class Transaction. Lines 23–24 retrieve the available balance of the account involved by invoking member function getAvailableBalance of bankDatabase. Note that line 24 uses inherited member function getAccountNumber to get the account number of the current user, which it then passes to getAvailableBalance. Lines 27–28 retrieve the total balance of the current user’s account. Lines 31–36 display the balance information on the ATM’s screen. Recall that displayDollarAmount takes a double argument and outputs it to the screen formatted as a dollar amount. For example, if a user’s availableBalance is 700.5, line 33 outputs $700.50. Note that line 36 inserts a blank line of output to separate the balance information from subsequent output (i.e., the main menu repeated by class ATM after executing the BalanceInquiry).

Fig. E.18 BalanceInquiry class member-function definitions.

 1   // BalanceInquiry.cpp
 2   // Member-function definitions for class BalanceInquiry.
 3   #include "BalanceInquiry.h" // BalanceInquiry class definition
 4   #include "Screen.h" // Screen class definition
 5   #include "BankDatabase.h" // BankDatabase class definition
 6
 7   // BalanceInquiry constructor initializes base-class data members
 8   BalanceInquiry:: BalanceInquiry( int userAccountNumber, Screen &atmScreen,
 9      BankDatabase &atmBankDatabase )
10      : Transaction( userAccountNumber, atmScreen, atmBankDatabase )
11   {
12      // empty body
13   } // end BalanceInquiry constructor
14
15   // performs transaction; overrides Transaction's pure virtual function
16   void BalanceInquiry::execute()
17   {
18      // get references to bank database and screen
19      BankDatabase &bankDatabase = getBankDatabase();
20      Screen &screen = getScreen();
21
22      // get the available balance for the current user's Account
23      double availableBalance =
24         bankDatabase.getAvailableBalance( getAccountNumber() );
25
26      // get the total balance for the current user's Account
27      double totalBalance =
28         bankDatabase.getTotalBalance( getAccountNumber() );
29
30      // display the balance information on the screen
31      screen.displayMessageLine( " Balance Information:" );
32      screen.displayMessage( " - Available balance: " );
33      screen.displayDollarAmount( availableBalance );
34      screen.displayMessage( "  - Total balance:     " );
35      screen.displayDollarAmount( totalBalance );
36      screen.displayMessageLine( "" );
37   } // end function execute

E.11 Class Withdrawal

Class Withdrawal (Figs. E.19E.20) derives from Transaction and represents a withdrawal ATM transaction. Figure E.19 expands upon the header file for this class developed in Fig. 13.31. Class Withdrawal has a constructor and one member function execute, which we discuss shortly. Recall from the class diagram of Fig. 13.29 that class Withdrawal has one attribute, amount, which line 16 implements as an int data member. Figure 13.28 models associations between class Withdrawal and classes Keypad and CashDispenser, for which lines 17–18 implement references keypad and cashDispenser, respectively. Line 19 is the function prototype of a private utility function that we soon discuss.

Fig. E.19 Withdrawal class definition.

 1   // Withdrawal.h
 2   // Withdrawal class definition. Represents a withdrawal transaction.
 3   #ifndef WITHDRAWAL_H
 4   #define WITHDRAWAL_H
 5
 6   #include "Transaction.h" // Transaction class definition
 7   class Keypad; // forward declaration of class Keypad
 8   class CashDispenser; // forward declaration of class CashDispenser
 9
10   class Withdrawal : public Transaction
11   {
12   public:
13      Withdrawal( int, Screen &, BankDatabase &, Keypad &, CashDispenser & );
14      virtual void execute(); // perform the transaction
15   private:
16      int amount; // amount to withdraw
17      Keypad &keypad; // reference to ATM's keypad
18      CashDispenser &cashDispenser; // reference to ATM's cash dispenser
19      int displayMenuOfAmounts() const; // display the withdrawal menu
20   }; // end class Withdrawal
21
22   #endif // WITHDRAWAL_H

Withdrawal Class Member-Function Definitions

Figure E.20 contains the member-function definitions for class Withdrawal. Line 3 #includes the class’s definition, and lines 4–7 #include the definitions of the other classes used in Withdrawal’s member functions. Line 11 declares a global constant corresponding to the cancel option on the withdrawal menu. We’ll soon discuss how the class uses this constant.

Fig. E.20 Withdrawal class member-function definitions.

 1   // Withdrawal.cpp
 2   // Member-function definitions for class Withdrawal.
 3   #include "Withdrawal.h" // Withdrawal class definition
 4   #include "Screen.h" // Screen class definition
 5   #include "BankDatabase.h" // BankDatabase class definition
 6   #include "Keypad.h" // Keypad class definition
 7   #include "CashDispenser.h" // CashDispenser class definition
 8
 9   // global constant that corresponds to menu option to cancel
10   const static int CANCELED = 6;
11
12   // Withdrawal constructor initialize class's data members
13   Withdrawal::Withdrawal( int userAccountNumber, Screen &atmScreen,
14      BankDatabase &atmBankDatabase, Keypad &atmKeypad,
15      CashDispenser &atmCashDispenser )
16      : Transaction( userAccountNumber, atmScreen, atmBankDatabase ),
17        keypad( atmKeypad ), cashDispenser( atmCashDispenser )
18   {
19      // empty body
20   } // end Withdrawal constructor
21
22   // perform transaction; overrides Transaction's pure virtual function
23   void Withdrawal::execute()
24   {
25      bool cashDispensed = false; // cash was not dispensed yet
26      bool transactionCanceled = false; // transaction was not canceled yet
27
28      // get references to bank database and screen
29      BankDatabase &bankDatabase = getBankDatabase();
30      Screen &screen = getScreen();
31
32      // loop until cash is dispensed or the user cancels
33      do
34      {
35         // obtain the chosen withdrawal amount from the user
36         int selection = displayMenuOfAmounts();
37
38         // check whether user chose a withdrawal amount or canceled
39         if ( selection != CANCELED )
40         {
41            amount = selection; // set amount to the selected dollar amount
42
43            // get available balance of account involved
44            double availableBalance =
45               bankDatabase.getAvailableBalance( getAccountNumber() );
46
47            // check whether the user has enough money in the account
48            if ( amount <= availableBalance )
49            {
50               // check whether the cash dispenser has enough money
51               if ( cashDispenser.isSufficientCashAvailable( amount ) )
52               {
53                  // update the account involved to reflect withdrawal
54                  bankDatabase.debit( getAccountNumber(), amount );
55
56                  cashDispenser.dispenseCash( amount ); // dispense cash
57                  cashDispensed = true; // cash was dispensed
58
59                  // instruct user to take cash
60                  screen.displayMessageLine(
61                     " Please take your cash from the cash dispenser." );
62               } // end if
63               else // cash dispenser does not have enough cash
64                  screen.displayMessageLine(
65                     " Insufficient cash available in the ATM."
66                     " Please choose a smaller amount." );
67            } // end if
68            else // not enough money available in user's account
69            {
70               screen.displayMessageLine(
71                  " Insufficient funds in your account."
72                  " Please choose a smaller amount." );
73            } // end else
74         } // end if
75         else // user chose cancel menu option
76         {
77            screen.displayMessageLine( " Canceling transaction..." );
78            transactionCanceled = true; // user canceled the transaction
79         } // end else
80      } while ( !cashDispensed && !transactionCanceled ); // end do...while
81   } // end function execute
82
83   // display a menu of withdrawal amounts and the option to cancel;
84   // return the chosen amount or 0 if the user chooses to cancel
85   int Withdrawal::displayMenuOfAmounts() const
86   {
87      int userChoice = 0; // local variable to store return value
88
89      Screen &screen = getScreen(); // get screen reference
90
91      // array of amounts to correspond to menu numbers
92      int amounts[] = { 02040, 60100200 };
93
94      // loop while no valid choice has been made
95      while ( userChoice == 0 )
96      {
97         // display the menu
98         screen.displayMessageLine( " Withdrawal options:" );
99         screen.displayMessageLine( "1 - $20" );
100        screen.displayMessageLine( "2 - $40" );
101        screen.displayMessageLine( "3 - $60" );
102        screen.displayMessageLine( "4 - $100" );
103        screen.displayMessageLine( "5 - $200" );
104        screen.displayMessageLine( "6 - Cancel transaction" );
105        screen.displayMessage( " Choose a withdrawal option (1-6): " );
106
107        int input = keypad.getInput(); // get user input through keypad
108
109        // determine how to proceed based on the input value
110        switch ( input )
111        {
112           case 1: // if the user chose a withdrawal amount
113           case 2: // (i.e., chose option 1, 2, 3, 4 or 5), return the
114           case 3: // corresponding amount from amounts array
115           case 4:
116           case 5:
117              userChoice = amounts[ input ]; // save user's choice
118              break;
119           case CANCELED: // the user chose to cancel
120              userChoice = CANCELED; // save user's choice
121              break;
122           default: // the user did not enter a value from 1-6
123              screen.displayMessageLine(
124                 " Ivalid selection. Try again." );
125        } // end switch
126     } // end while
127
128     return userChoice; // return withdrawal amount or CANCELED
129  } // end function displayMenuOfAmounts

Class Withdrawal’s constructor (defined in lines 13–20 of Fig. E.20) has five parameters. It uses a base-class initializer in line 16 to pass parameters userAccountNumber, atm-Screen and atmBankDatabase to base class Transaction’s constructor to set the data members that Withdrawal inherits from Transaction. The constructor also takes references atmKeypad and atmCashDispenser as parameters and assigns them to reference data members keypad and cashDispenser using member initializers (line 17).

Class Withdrawal overrides Transaction’s pure virtual function execute with a concrete implementation (lines 23–81) that performs the steps involved in a withdrawal. Line 25 declares and initializes a local bool variable cashDispensed. This variable indicates whether cash has been dispensed (i.e., whether the transaction has completed successfully) and is initially false. Line 26 declares and initializes to false a bool variable transactionCanceled that indicates whether the transaction has been canceled by the user. Lines 29–30 get references to the bank database and the ATM’s screen by invoking member functions inherited from base class Transaction.

Lines 33–80 contain a do...while statement that executes its body until cash is dispensed (i.e., until cashDispensed becomes true) or until the user chooses to cancel (i.e., until transactionCanceled becomes true). This loop continuously returns the user to the start of the transaction if an error occurs (i.e., the requested withdrawal amount is greater than the user’s available balance or greater than the amount of cash in the cash dispenser). Line 36 displays a menu of withdrawal amounts and obtains a user selection by calling private utility function displayMenuOfAmounts (defined in lines 85–129). This function displays the menu of amounts and returns either an int withdrawal amount or the int constant CANCELED to indicate that the user has chosen to cancel the transaction.

Member function displayMenuOfAmounts (lines 85–129) first declares local variable userChoice (initially 0) to store the value that the member function will return (line 87). Line 89 gets a reference to the screen by calling member function getScreen inherited from base class Transaction. Line 92 declares an integer array of withdrawal amounts that correspond to the amounts displayed in the withdrawal menu. We ignore the first element in the array (index 0) because the menu has no option 0. The while statement in lines 95–126 repeats until userChoice takes on a value other than 0. We’ll see shortly that this occurs when the user makes a valid selection from the menu. Lines 98–105 display the withdrawal menu on the screen and prompt the user to enter a choice. Line 107 obtains integer input through the keypad. The switch statement in lines 110–125 determines how to proceed based on the user’s input. If the user selects a number between 1 and 5, line 117 sets userChoice to the value of the element in amounts at index input. For example, if the user enters 3 to withdraw $60, line 117 sets userChoice to the value of amounts[ 3 ] (i.e., 60). Line 118 terminates the switch. Variable userChoice no longer equals 0, so the while in lines 95–126 terminates and line 128 returns userChoice. If the user selects the cancel menu option, lines 120–121 execute, setting userChoice to CANCELED and causing the member function to return this value. If the user does not enter a valid menu selection, lines 123–124 display an error message and the user is returned to the withdrawal menu.

The if statement in line 39 in member function execute determines whether the user has selected a withdrawal amount or chosen to cancel. If the user cancels, lines 77–78 execute to display an appropriate message to the user and set transactionCanceled to true. This causes the loop-continuation test in line 80 to fail and control to return to the calling member function (i.e., ATM member function performTransactions). If the user has chosen a withdrawal amount, line 41 assigns local variable selection to data member amount. Lines 44–45 retrieve the available balance of the current user’s Account and store it in a local double variable availableBalance. Next, the if statement in line 48 determines whether the selected amount is less than or equal to the user’s available balance. If it is not, lines 70–72 display an appropriate error message. Control then continues to the end of the do...while, and the loop repeats because both cashDispensed and transactionCanceled are still false. If the user’s balance is high enough, the if statement in line 51 determines whether the cash dispenser has enough money to satisfy the withdrawal request by invoking the cashDispenser’s isSufficientCashAvailable member function. If this member function returns false, lines 64–66 display an appropriate error message and the do...while repeats. If sufficient cash is available, then the requirements for do the withdrawal are satisfied, and line 54 debits amount from the user’s account in the database. Lines 56–57 then instruct the cash dispenser to dispense the cash to the user and set cashDispensed to true. Finally, lines 60–61 display a message to the user that cash has been dispensed. Because cashDispensed is now true, control continues after the do...while. No additional statements appear below the loop, so the member function returns control to class ATM.

In the function calls in lines 64–66 and lines 70–72, we divide the argument to Screen member function displayMessageLine into two string literals, each placed on a separate line in the program. We do so because each argument is too long to fit on a single line. C++ concatenates (i.e., combines) string literals adjacent to each other, even if they are on separate lines. For example, if you write "Happy " "Birthday" in a program, C++ will view these two adjacent string literals as the single string literal "Happy Birthday". As a result, when lines 64–66 execute, displayMessageLine receives a single string as a parameter, even though the argument in the function call appears as two string literals.

E.12 Class Deposit

Class Deposit (Figs. E.21E.22) derives from Transaction and represents a deposit ATM transaction. Figure E.21 contains the Deposit class definition. Like derived classes BalanceInquiry and Withdrawal, Deposit declares a constructor (line 13) and member function execute (line 14)—we discuss these momentarily. Recall from the class diagram of Fig. 13.29 that class Deposit has one attribute amount, which line 16 implements as an int data member. Lines 17–18 create reference data members keypad and depositSlot that implement the associations between class Deposit and classes Keypad and DepositSlot modeled in Fig. 13.28. Line 19 contains the function prototype for a private utility function promptForDepositAmount that we’ll discuss shortly.

Fig. E.21 Deposit class definition.

 1   // Deposit.h
 2   // Deposit class definition. Represents a deposit transaction.
 3   #ifndef DEPOSIT_H
 4   #define DEPOSIT_H
 5
 6   #include "Transaction.h" // Transaction class definition
 7   class Keypad; // forward declaration of class Keypad
 8   class DepositSlot; // forward declaration of class DepositSlot
 9
10   class Deposit : public Transaction
11   {
12   public:
13      Deposit( int, Screen &, BankDatabase &, Keypad &, DepositSlot & );
14      virtual void execute(); // perform the transaction
15   private:
16      double amount; // amount to deposit
17      Keypad &keypad; // reference to ATM's keypad
18      DepositSlot &depositSlot; // reference to ATM's deposit slot
19      double promptForDepositAmount() const; // get deposit amount from user
20   }; // end class Deposit
21
22   #endif // DEPOSIT_H

Deposit Class Member-Function Definitions

Figure E.22 presents the Deposit class implementation. Line 3 #includes the Deposit class definition, and lines 4–7 #include the class definitions of the other classes used in Deposit’s member functions. Line 9 declares a constant CANCELED that corresponds to the value a user enters to cancel a deposit. We’ll soon discuss how the class uses this constant.

Fig. E.22 Deposit class member-function definitions.

 1   // Deposit.cpp
 2   // Member-function definitions for class Deposit.
 3   #include "Deposit.h" // Deposit class definition
 4   #include "Screen.h" // Screen class definition
 5   #include "BankDatabase.h" // BankDatabase class definition
 6   #include "Keypad.h" // Keypad class definition
 7   #include "DepositSlot.h" // DepositSlot class definition
 8
 9   const static int CANCELED = 0; // constant representing cancel option
10
11   // Deposit constructor initializes class's data members
12   Deposit::Deposit( int  userAccountNumber, Screen &atmScreen,
13      BankDatabase &atmBankDatabase, Keypad &atmKeypad,
14      DepositSlot &atmDepositSlot )
15      : Transaction( userAccountNumber, atmScreen, atmBankDatabase ),
16        keypad( atmKeypad ), depositSlot( atmDepositSlot )
17   {
18      // empty body
19   } // end Deposit constructor
20
21   // performs transaction; overrides Transaction's pure virtual function
22   void Deposit::execute()
23   {
24      BankDatabase &bankDatabase = getBankDatabase(); // get reference
25      Screen &screen = getScreen(); // get reference
26
27      amount = promptForDepositAmount(); // get deposit amount from user
28
29      // check whether user entered a deposit amount or canceled
30      if ( amount != CANCELED )
31      {
32         // request deposit envelope containing specified amount
33         screen.displayMessage(
34           " Please insert a deposit envelope containing " );
35         screen.displayDollarAmount( amount );
36         screen.displayMessageLine( " in the deposit slot." );
37
38         // receive deposit envelope
39         bool envelopeReceived = depositSlot.isEnvelopeReceived();
40
41         // check whether deposit envelope was received
42         if ( envelopeReceived )
43         {
44            screen.displayMessageLine( " Your envelope has been received."
45               " NOTE: The money deposited will not be available until we"
46               " verify the amount of any enclosed cash, and any enclosed "
47               "checks clear." );
48
49            // credit account to reflect the deposit
50            bankDatabase.credit( getAccountNumber(), amount );
51         } // end if
52         else // deposit envelope not received
53         {

54            screen.displayMessageLine( " You did not insert an "
55               "envelope, so the ATM has canceled your transaction." );
56         } // end else
57      } // end if
58      else // user canceled instead of entering amount
59      {
60         screen.displayMessageLine( " Canceling transaction..." );
61      } // end else
62   } // end function execute
63
64   // prompt user to enter a deposit amount in cents
65   double Deposit::promptForDepositAmount() const
66   {
67      Screen &screen = getScreen(); // get reference to screen
68
69      // display the prompt and receive input
70      screen.displayMessage( " Please enter a deposit amount in "
71         "CENTS (or 0 to cancel): " );
72      int input = keypad.getInput(); // receive input of deposit amount
73
74      // check whether the user canceled or entered a valid amount
75      if ( input == CANCELED )
76         return CANCELED;
77      else
78      {
79         return static_castdouble >( input ) / 100; // return dollar amount
80      } // end else
81   } // end function promptForDepositAmount

Like class Withdrawal, class Deposit contains a constructor (lines 12–19) that passes three parameters to base class Transaction’s constructor using a base-class initializer (line 15). The constructor also has parameters atmKeypad and atmDepositSlot, which it assigns to its corresponding data members (line 16).

Member function execute (lines 22–62) overrides pure virtual function execute in base class Transaction with a concrete implementation that performs the steps required in a deposit transaction. Lines 24–25 get references to the database and the screen. Line 27 prompts the user to enter a deposit amount by invoking private utility function promptForDepositAmount (defined in lines 65–81) and sets data member amount to the value returned. Member function promptForDepositAmount asks the user to enter a deposit amount as an integer number of cents (because the ATM’s keypad does not contain a decimal point; this is consistent with many real ATMs) and returns the double value representing the dollar amount to be deposited.

Line 67 in member function promptForDepositAmount gets a reference to the ATM’s screen. Lines 70–71 display a message on the screen asking the user to input a deposit amount as a number of cents or “0” to cancel the transaction. Line 72 receives the user’s input from the keypad. The if statement in lines 75–80 determines whether the user has entered a real deposit amount or chosen to cancel. If the user chooses to cancel, line 76 returns the constant CANCELED. Otherwise, line 79 returns the deposit amount after converting from the number of cents to a dollar amount by casting input to a double, then dividing by 100. For example, if the user enters 125 as the number of cents, line 79 returns 125.0 divided by 100, or 1.25—125 cents is $1.25.

The if statement in lines 30–61 in member function execute determines whether the user has chosen to cancel the transaction instead of entering a deposit amount. If the user cancels, line 60 displays an appropriate message, and the member function returns. If the user enters a deposit amount, lines 33–36 instruct the user to insert a deposit envelope with the correct amount. Recall that Screen member function displayDollarAmount outputs a double formatted as a dollar amount.

Line 39 sets a local bool variable to the value returned by depositSlot’s isEnvelopeReceived member function, indicating whether a deposit envelope has been received. Recall that we coded isEnvelopeReceived (lines 7–10 of Fig. E.10) to always return true, because we are simulating the functionality of the deposit slot and assume that the user always inserts an envelope. However, we code member function execute of class Deposit to test for the possibility that the user does not insert an envelope—good software engineering demands that programs account for all possible return values. Thus, class Deposit is prepared for future versions of isEnvelopeReceived that could return false. Lines 44–50 execute if the deposit slot receives an envelope. Lines 44–47 display an appropriate message to the user. Line 50 then credits the deposit amount to the user’s account in the database. Lines 54–55 will execute if the deposit slot does not receive a deposit envelope. In this case, we display a message to the user stating that the ATM has canceled the transaction. The member function then returns without modifying the user’s account.

E.13 Test Program ATMCaseStudy.cpp

ATMCaseStudy.cpp (Fig. E.23) is a simple C++ program that allows us to start, or “turn on,” the ATM and test the implementation of our ATM system model. The program’s main function (lines 6–11) does nothing more than instantiate a new ATM object named atm (line 8) and invoke its run member function (line 9) to start the ATM.

Fig. E.23 ATMCaseStudy.cpp starts the ATM system.

 1   // ATMCaseStudy.cpp
 2   // Driver program for the ATM case study.
 3   #include "ATM.h" // ATM class definition
 4
 5   // main function creates and runs the ATM
 6   int main()
 7   {
 8      ATM atm; // create an ATM object
 9      atm.run(); // tell the ATM to start
10      return 0;
11   } // end main

E.14 Wrap-Up

Congratulations on completing the entire Software Engineering ATM Case Study! We hope you found this experience to be valuable and that it reinforced many of the concepts that you learned in Chapters 113. We would sincerely appreciate your comments, criticisms and suggestions. You can reach us at [email protected]. We’ll respond promptly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset