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 1–7, 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.
Class ATM
(Figs. E.1–E.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.
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.20—currentTransaction
—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.9–E.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.
Class Screen
(Figs. E.3–E.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.
Figure E.4 contains the member-function definitions for class Screen
. Line 11 #include
s 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.
Class Keypad
(Figs. E.5–E.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.
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
Class CashDispenser
(Figs. E.7–E.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
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
.
Class DepositSlot
(Figs. E.9–E.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
.
Class Account
(Figs. E.11–E.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
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.
Class BankDatabase
(Figs. E.13–E.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
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( 12345, 54321, 1000.0, 1200.0 );
10 Account account2( 98765, 56789, 200.0, 200.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.
Class Transaction
(Figs. E.15–E.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.
Class BalanceInquiry
(Figs. E.17–E.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 #include
s 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
Class Withdrawal
(Figs. E.19–E.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
Figure E.20 contains the member-function definitions for class Withdrawal
. Line 3 #include
s 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[] = { 0, 20, 40, 60, 100, 200 };
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.
Class Deposit
(Figs. E.21–E.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
Figure E.22 presents the Deposit
class implementation. Line 3 #include
s 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_cast< double >( 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.
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
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 1–13. We would sincerely appreciate your comments, criticisms and suggestions. You can reach us at [email protected]. We’ll respond promptly.