The Atm.cpp File

 <  Free Open Study  >  

 // Atm.cpp: The source file of the main classes composing the ATM  // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include <iostream.h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\%s''; const char* file_move_format = ''copy %s\%s %s\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) {      while (*account != ' 
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
') { if (*account< '0' *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy(name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf(&buf[7], ''%4s'', pin); pin[4] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush(stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled?getchar() : '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; keypad->disable(); return(strncmp(pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '
 // Atm.cpp: The source file of the main classes composing the ATM // side of the application. It consists of all method and global // data definitions required by these classes. #include <stdio.h> #include < iostream .h> #include <string.h> #include <math.h> #include ''network.hpp'' #include ''atm.hpp'' #include ''trans.hpp'' // Definition of the two card slots in the ATM system, the // card reader's slot, where a user inserts his or her card, // and the ATM' s slot into which eaten cards are taken. // These are simulated in this system by directories in the // file system. The two path names are given to the ATM application // as its first two command-line arguments. char CardSlots[large_string]; char ATMSlots[large_string]; // The definition of the two format strings for simulating // the ejecting and eating of bank cards. Can be changed to // the equivalent Unix commands for portability. const char* file_delete_format = ''del %s\\%s''; const char* file_move_format = ''copy %s\\%s %s\\%s.%d''; // The checking of an account name determines that it consists of // numeric digits. (Note: The actual account on the Bank side of the // application sees seven digits plus a terminating S or C for // savings and checking, respectively). int bad_account(char* account) { while (*account != '\0') { if (*account< '0'   *account > '9') { return(1); } account++; } return(0); } // For now PIN numbers and account numbers use the same algorithm. // They may drift in the future. int bad_pin(char* pin) { return(bad_account(pin)); } // Each PhysicalCardReader has a name, which it uses as the name of // the BankCardfile when it is inserted into CardSlot directory. // This naming would not be necessary in a real system, since the // hardware would take care of this naming problem. It appears in // this application only for simplifying the simulation. PhysicalCardReader::PhysicalCardReader(char* n) { strcpy (name, n); } // The readinfo method tries to open a file in the CardSlots // directory with the name of the card reader. This name // would not be needed in a real system. The encoded data // would be read off the card reader's hardware. The method // returns a one if the card cannot be read, zero if it // was read successfully. The buf argument is filled in // with the first line of the file on success. It is assumed // to contain a seven-digit numeric string (account number) // followed by a four-digit numeric string (pin number). int PhysicalCardReader::readinfo(char* buf) { FILE* fd; sprintf(buf, ''%s/%s'', CardSlots, name); if ((fd = fopen(buf, ''r'')) == NULL) { return(1); } fgets(buf, large_string, fd); fclose(fd); return(0); } // The simulation for eject cards is to remove the file from the card // slot directory. In a real ATM system, this method would be a call // to a hardware driver. void PhysicalCardReader::eject_card() { char buf[large_string]; sprintf(buf, file_delete_format, CardSlots, name); system(buf); } // The simulation for eating cards is to move the BankCard file from // the CardSlot directory to the ATM slot directory. In a real ATM // system, this method would be a call to a hardware driver. void PhysicalCardReader::eat_card() { char buf[large_string]; static int count=1; sprintf(buf, file_move_format, CardSlots, name, ATMSlots, name, count++); system(buf); } // The constructor for CardReader calls the constructor of its //PhysicalCardReader. CardReader::CardReader(char* name) : cardReader(name) { validcard = 0; account[0] = pin[0] = '\0'; } // The read_card method checks to see if there is a card in // the slot. If there isn't, it returns 1. If there is and // it isn't readable, then the card is rejected and a 1 is // returned. If the data on the card is readable, then the account // and PIN number are read from the card (account is assumed to be // a seven-character numeric string, the PIN a four-digit numeric // string). If the data cannot be parsed (the card is not a valid // bank card), then a 1 is returned and the card is ejected. int CardReader::read_card() { char buf[large_string]; validcard = 0; switch (cardReader.readinfo(buf)) { case -1: // If the card couldn't be read, then eject it. cardReader.eject_card(); return(1); case 1: // If there is no card, then report it to ATM. return(1); case 0: // We have the information, parse it. // If the account number is bad, return 1 and eject. sscanf(buf, ''%7s'', account); account[7] = '\0'; if (bad_account(account)) { cardReader.eject_card(); return(1); } //If the PIN number is bad, return 1 and eject. sscanf (&buf[7], ''%4s'', pin); pin[4] = '\0'; if (bad_pin(pin)) { cardReader.eject_card(); return(1); } } validcard= 1; return(0); } // The accessor methods are required for verifying a user- // supplied PIN number and building transactions. These are // valid since there is a design situation facing policy between // two separate key abstractions, i.e., the SuperKeypad and // the CardReader. Both return 0 on success, 1 on failure. int CardReader::get_account(char* acc) { if (validcard) { strcpy(acc, account); } return(!validcard); } int CardReader::get_pin(char* p) { if (validcard) { strcpy(p, pin); } return(!validcard); } // The following two methods simply delegate to their wrapped // PhysicalCardReader class and execute its methods. void CardReader::eject_card() { cardReader.eject_card(); } void CardReader::eat_card() { cardReader.eat_card(); } Keypad::Keypad() { enabled = 0; } void Keypad::enable() { fflush (stdin); enabled =1; } void Keypad::disable() { enabled = 0; } // The getkey method reads a single character from the Keypad // (in the simulation, the hardware is assumed to be the standard // input). We assume the newline character to be the Enter key, // implying that all input has been received. The method returns // the character read on success, NULL terminator if the keypad // is not enabled. char Keypad::getkey() { return(enabled? getchar () : '\0'); } void DisplayScreen::display_msg(const char* msg) { cout << ''@ATM Display@ '' << msg; } SuperKeypad::SuperKeypad() { keypad = new Keypad; display = new DisplayScreen; } SuperKeypad::~SuperKeypad() { delete keypad; delete display; } // This method delegates to its contained display screen. Such // noncommunicating behavior is an argument for splitting the // SuperKeypad class. However, the verify_pin()and // get_transaction()methodsprovide more than enough cohesion // of data to justify the SuperKeypad's existence. void SuperKeypad::display_msg(const char* msg) { display->display_msg(msg); } // The verify_pin method enables the keypad, prompts the user // for a PIN number, and checks it against the user-supplied // PIN number. The method returns zero on success, nonzero // on failure. int SuperKeypad::verify_pin(const char* pin_to_verify) { char pin[small_string]; int i = 0; keypad->enable(); display->display_msg(''Enter Pin Number: ''); while ((pin[i++] = keypad->getkey()) != EnterKey) ; pin[i]= '\0'; keypad->disable(); return( strncmp (pin,pin_to_verify,4)); } // Note the case analysis on the type of transaction. This case // analysis is necessary since our object-oriented design has // bumped up against an action-oriented (text-menu driven) user // interface as per our discussion in Chapter 9. At least this case // analysis is restricted to one point in the design (one method) // and hidden in the SuperKeypad class. Any classes higher in the // system are oblivious to the case analysis. Transaction* SuperKeypad::get_transaction(char* account, char* pin) { int i = 0; char amount_str[small_string], trans_type; char target_account[small_string]; double amount; keypad->enable(); do{ display->display_msg(''Select a Transaction\n''); display->display_msg(''\tW)ithdrawal\n''); display->display_msg(''\tD)eposit\n''); display->display_msg(''\tB)alance\n''); display->display_msg(''\tT)ransfer\n''); display->display_msg(''\tQ)uit\n''); trans_type = keypad->getkey(); while (keypad->getkey() != EnterKey) ; } while (trans_type != 'W' && trans_type != 'D' && trans_type != 'B' && trans_type != 'T' && trans_type != 'Q'); if (trans_type == 'Q') { return(NULL); } display->display_msg(''Enter Account Type (S/C): ''); account[7] = keypad->getkey(); account[8] = '\0'; while (keypad->getkey() != EnterKey) ; if (trans_type != 'B') { display->display_msg(''Enter Amount: ''); while ((amount_str[i++] = keypad->getkey()) != EnterKey) ; amount_str[i-1] = '\0'; amount = atof(amount_str); } if (trans_type == 'T') { display->display_msg(''Enter Target Account Number: ''); i=0; while ((target_account[i++] = keypad->getkey()) != EnterKey) ; target_account[i-1] = '\0'; display->display_msg(''Enter Target Account Type (S/ C) : ''); target_account[7] = keypad->getkey(); target_account[8] = '\0'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of $10. The reader may want to // elaborate on this class by giving it fixed numbers of $20 bills, // $10 's, $5's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of $10 bills and will dispense only multiples of $20. All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser , this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows : \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout ) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified , this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically , in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); } 
'; while (keypad->getkey() != EnterKey) ; } switch (trans_type) { case 'W' : return(new Withdraw(account, pin, amount)); case 'D': return(new Deposit(account, pin, amount)); case 'B': return(new Balance(account, pin)); case 'T': return(new Transfer(account, pin, target_account, amount)); default: cerr << ''Unknown type in get_transaction switch statement\n''; return(NULL); } } CashDispenser::CashDispenser(int initial_cash) { cash_on_hand = initial_cash; } int CashDispenser::enough_cash(int amount) { return(amount <= cash_on_hand); } // We can give out only multiples of . The reader may want to // elaborate on this class by giving it fixed numbers of bills, // 's, 's, etc. Some ATMs allow for the dispensing of stamps, // theater tickets, etc., as well. Many warn the user that they are // out of bills and will dispense only multiples of . All of // these items can be added to this class without impact on the rest // of the system. int CashDispenser::dispense(int amount) { amount -= amount % 10; if (enough_cash(amount)) { cout << ''@CashDispenser@ Giving the user '' << amount << '' cash\n''; return(0); } return(1); } int DepositSlot::retrieve_envelope() { cout << ''@DepositSlot@ Getting an envelope from the user\n''; return(0); } // The receipt printer simulates the printing of receipts by // creating a Receipts file in the current working directory. // Again, the reader can elaborate on this class, adding a number // of error checks, paper availability, etc. Like the cash // dispenser, this is left as an exercise // to the reader since it // adds no pedagogical benefit to this example. void ReceiptPrinter::print(TransactionList* translist) { FILE* fd; cout << ''@ReceiptPrinter@ Your receipt is as follows: \n''; if ((fd = fopen(''receipt'', ''w'')) == NULL) { fd = stdout; } translist->print(fd); if (fd != stdout) { fclose(fd); } } // The BankProxy is an extremely important class. It is the // representative of the Bank class within the ATM application. It // is merely a wrapper for the Network class, which is a wrapper // itself for which transport mechanism a distributed process is // going to use for communication. In this example, I chose to // simulate the network, but readers are free to use any network // or byte-transfer mechanism they wish. The application is // completely independent of this mechanism. (Note: Changes in the // byte-transfer mechanism affect only the Network class's // implementation.) BankProxy::BankProxy(Network* n) { network = n; } // When a BankProxy needs to process a transaction, it asks its // Network object to send it. Assuming the send works correctly, // the method then asks the Network for a response, which takes the // form of a status integer (0,1, indicating success or failure on // part of the real Bank class living in the bank's application // space). If other Transaction application-specific data is // required, then it is sent to the appropriate transaction's // update message. This is to allow the Bank to update the state of // a transaction in the ATM's application space from changes // generated from the Bank's application space. Currently, only // the Balance derived transaction uses this method to update its // balance from the account in the Bank's application space. int BankProxy::process(Transaction* t) { int status, count; char other_info[small_string]; if (network-> send(t)) { return(1); } count = network->receive(status, other_info); if (count) { t->update(other_info, count); } return(status); } // Anew ATM object is given its Bank Proxy, a name to be handed down // to its PhysicalCardReader (only needed for simulation), and its // initial cash. ATM::ATM(BankProxy* b, char* name, int cash) { bank = new BankProxy(*b); cardReader = new CardReader(name); superKeypad = new SuperKeypad; cashDispenser = new CashDispenser(cash); depositSlot = new DepositSlot; receiptPrinter = new ReceiptPrinter; translist = new TransactionList(max_transaction_atm); } ATM::~ATM() { delete bank; delete cardReader; delete superKeypad; delete cashDispenser; delete depositSlot; delete receiptPrinter; delete translist; } // The activate method for the ATM class is the main driver for the // ATM objects. This method puts up the welcome message and waits // for a card to become available (in simulation, a card becomes // available when a user copies a file with the PhysicalCard- // Reader's name into the CardSlots directory). When a card is // available, the ATM retrieves the account and PIN number from the // CardReader. It then asks its SuperKeypad to verify the PIN. // The SuperKeypad verifies the PIN by getting a PIN number from the // user and ensuring that it equals the one from the card. The // actual check will be done by the Bank, which ensures that the PIN // is equal to the one stored in the Account object. // Once the PIN is verified, this method asks the SuperKeypad to // collect and build a transaction. It preprocesses the // transaction (handling things like getting envelopes for // deposits, checking the cash dispenser to ensure enough cash is // available for a withdrawal, etc.). If preprocessing was // successful, it then asks its BankProxy to process the // transaction. This method packages up the transaction, ships it // over the network to the Bank's application, and collects a // response from the Bank's application via the network. Notice // the transparent nature of the interprocess communication. At // design time we were able to completely ignore this distributed // processing. Assuming the processing went well, we then execute // a postprocess, which performs tasks like giving the user his or // her money, etc. This method repeats the processing of the // transaction until the user selects Quit, which requires the // SuperKeypad::get_transaction method to return NULL. At this // time the receipt printer generates a receipt and ejects the // card. void ATM::activate() { char account[small_string], pin[small_string]; int count = 1, verified; Transaction* trans; while (1) { superKeypad->display_msg(''Welcome to the Bank of Heuristics!!!\n''); superKeypad->display_msg(''Please Insert Your Card In the Card Reader\n''); // Get a card. while (cardReader->read_card()!=0) { ; } cardReader->get_account(account); cardReader->get_pin(pin); // Try three times to verify the PIN number. do { verified = superKeypad->verify_pin(pin); } while (verified != 0 && count++ < 3); // If it couldn't be verified, then eat the card. if (verified != 0) { superKeypad->display_msg(''Sorry, three strikes and you're out!!! \n''); cardReader->eat_card(); } else { // Otherwise, keep getting Transactions until the user asks to // quit. // while ((trans = superKeypad->get_transaction(account, pin)) // != NULL) { // Preprocess the transaction, if necessary. The default is to do // nothing. // if (trans->preprocess(this) == 0) { // If preprocessing was successful, then process the Transaction. // If the Bank says the Transaction is valid, then add it to the // current list (for the receipt) and carry out any postprocessing. // if (bank->process(trans) == 0) // { translist->add_trans(trans); // trans->postprocess(this); } // If problems occur, display an appropriate message and continue. else { superKeypad->display_msg(''The Bank Refuses Your Transaction!\n''); superKeypad->display_msg(''Contact your Bank Representative.\n''); delete trans; } } else { superKeypad->display_msg(''This ATM is unable to comply with your''); superKeypad->display_msg('' request at this time.\n''); delete trans; } } //When we're done, print the receipt, clean up the Transaction // list, and eject the card. We're now ready to loop for another // user. receiptPrinter->print(translist); translist->cleanup(); cardReader->eject_card(); } } } // These are methods used by derived types of Transaction, // specifically, in their pre-/post-process methods. int ATM::retrieve_envelope() { return(depositSlot->retrieve_envelope()); } int ATM::enough_cash(double amount) { return(cashDispenser->enough_cash((int) amount)); } int ATM::dispense_cash(double amount) { return(cashDispenser->dispense((int) amount)); }
 <  Free Open Study  >  


Object-Oriented Design Heuristics
Object-Oriented Design Heuristics (paperback)
ISBN: 0321774965
EAN: 2147483647
Year: 1996
Pages: 180

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net