The Trans.cpp File
// Trans.cpp: The source code for the Transaction classes. The
// Transaction classes are interesting since they appear on both
// sides of the application. The Transaction objects on each side
// of the application could be completely different definitions.
// For convenience, they were made the same in this example. The
// parts of the public interface unique to one side of the
// application are restricted through the use of the BANK_SIDE or
// ATM_SIDE macros.
#define BANK_SIDE
#ifdef ATM_SIDE
#include ''atm.hpp''
#endif
#ifdef BANK_SIDE
#include ''bank.hpp''
#endif
#include ''trans.hpp''
// The TimeStamp constructor uses the time and ctime standard C
// library functions to create a simple string capturing data and
// time.
TimeStamp::TimeStamp()
{
time_t timer;
time(&timer);
strcpy(date_time, ctime(&timer));
}
void
TimeStamp::print(FILE* fd)
{
fprintf(fd, ''%s'', date_time);
}
Transaction::Transaction(char* account, char* p, double a)
{
strcpy(source_account, account);
strcpy(pin, p);
amount = a;
}
// These are protected accessor methods for use by the four derived
// classes of the Transaction class.
char*
Transaction::get_source_account()
{
return(source_account);
}
double
Transaction::get_amount()
{
return(amount);
}
void
Transaction::set_amount(double new_amount)
{
amount = new_amount;
}
void
Transaction::print(FILE* fd)
{
timeStamp.print(fd);
fprintf(fd, ''\tAccount:%s\tAmount:%4.21f\n'',
source_account,
amount);
}
#ifdef ATM_SIDE
// If the application is the ATM side, then define the preprocess,
// postprocess, and update methods to do nothing. The packetize
// method is responsible for building a string, which consists of a
// type field (four characters), the source account (seven
// characters), a space, a PIN number (four characters), a space,
// an amount (nine characters), a space, and a NULL terminator.
// This string is suitable for shipping across a network, or it can
// be transformed into something that can be shipped across a
// network.
int
Transaction::preprocess(ATM*)
{
return(0);
}
void
Transaction::postprocess(ATM*)
{
}
void
Transaction::update(char* info, int count)
{
}
void
Transaction::packetize(char* buf)
{
char type_buf[small_string];
strcpy(type_buf, type());
type_buf[4]='\0';
sprintf(buf, ''%s%s %s %4.21f'', type_buf, source_account,
pin, amount);
}
#endif
// If this is the Bank side of the application, include a
// verify_account method, which checks if an account's name and
// PIN match that of the Transaction.
#ifdef BANK_SIDE
int
Transaction::verify_account(Account* a)
{
return(a->verify_account(source_account, pin));
}
void
Transaction::packetize(int status, char* buf)
{
sprintf(buf, ''%4d %4.21f'', status, amount);
cout << ''Bank packetized network object: \'' '' << buf <<
''\''\n'';
}
#endif
Deposit::Deposit(char* account, char* p, double a) :
Transaction(account, p, a)
{
}
void
Deposit::print(FILE* fd)
{
fprintf(fd, ''Deposit: '');
Transaction::print(fd);
}
char*
Deposit::type()
{
return(''Deposit'');
}
// Preprocessing deposits requires grabbing an envelope. Nothing
// can go wrong with this in the simulation, but in the world of
// hardware we would have to consider timeouts, jammed envelopes,
// etc. In the real world, the DepositSlot::retrieve_envelope
// method would be implemented to handle these cases.
#ifdef ATM_SIDE
int
Deposit::preprocess(ATM* a)
{
return(a->retrieve_envelope());
}
#endif
// Processing a Deposit transaction involves getting the source
// account, verifying it, and then modifying the balance to reflect
// the deposited amount. This is a bit of a simplification from the
// real world, where a ''shadow'' account might be updated but a
// human needs to retrieve the envelope and check to be sure that
// money was actually included. This method suits our purposes,
// which is to show the design and implementation of a distributed
// process.
#ifdef BANK_SIDE
int
Deposit::process(AccountList* accounts)
{
Account* account = accounts->find_account(get_source_account());
if (account == NULL) {
return(1);
}
if (!Transaction::verify_account(account)) {
return(1);
}
account->modify_balance(get_amount());
return(0);
}
#endif
Withdraw::Withdraw(char* account, char* p, double a) :
Transaction(account, p, a)
{
}
void
Withdraw::print(FILE* fd)
{
fprintf(fd, ''Withdraw: '');
Transaction::print(fd);
}
char*
Withdraw::type()
{
return(''Withdraw'');
}
// Preprocessing a withdrawal involves checking the cash
// dispenser to be sure the ATM has enough cash. Postprocessing
// gives the user the cash.
#ifdef ATM_SIDE
int
Withdraw::preprocess(ATM* a)
{
return(!a->enough_cash(get_amount()));
}
void
Withdraw::postprocess(ATM* a)
{
a->dispense_cash(get_amount());
}
#endif
// The processing of a Withdraw transaction requires finding the
// source account, verifying the account, ensuring there is enough
// cash in the account, and, only then, reducing the amount of money
// in the account.
#ifdef BANK_SIDE
int
Withdraw::process(AccountList* accounts)
{
Account* account = accounts->find_account(get_source_account());
if (account == NULL || || !account->check_balance
(get_amount()))
{
return(1);
}
if (! Transaction::verify_account(account)) {
return(1);
}
account->modify_balance(-get_amount());
return(0);
}
#endif
Balance::Balance(char* account, char* p) : Transaction(account,
p, 0.0)
{
}
void
Balance::print(FILE* fd)
{
fprintf(fd, ''Balance: '');
Transaction::print(fd);
}
char*
Balance::type()
{
return(''Balance'');
}
// Processing a Balance involves finding the source account and
// copying its balance into the Amount field of the Transaction
// object. This assumes the account name and PIN were verified.
#ifdef BANK_SIDE
int
Balance::process(AccountList* accounts)
{
Account* account = accounts->find_account(get_source_account());
if (account == NULL) {
return(1);
}
if (!Transaction::verify_account(account)) {
return(1);
}
set_amount(account->get_balance());
return(0);
}
#endif
// The update method for Balance (on the ATM side of the
// application) is used to copy the retrieved balance back into the
// corresponding Transaction object on the ATM side.
#ifdef ATM_SIDE
void
Balance::update(char* info, int count)
{
set_amount(atof(info));
}
#endif
Transfer::Transfer(char* s_account, char* p, char* t_account,
double a)
: Transaction(s_account, p, a)
{
strcpy(target_account, t_account);
}
void
Transfer::print(FILE* fd)
{
fprintf(fd, ''Transfer: '');
Transaction::print(fd);
fprintf(fd, ''\tTarget Account: %s\n\n'', target_
account);
}
char*
Transfer::type()
{
return(''Transfer'');
}
// Packetizing a transfer implies including the target account at
// the end of the packet.
#ifdef ATM_SIDE
void
Transfer::packetize(char* buf)
{
Transaction::packetize(buf);
strcat(buf, '' '');
strcat(buf, target_account);
}
#endif
// Processing a Transfer object requires that we retrieve the
// source and target accounts, verify the source account, ensure
// that the source account has enough money for the transfer, and
// only then update the balances of the source (by subtracting the
// amount) and the target (by adding the equivalent amount).
#ifdef BANK_SIDE
int
Transfer::process(AccountList* accounts)
{
Account* src_account = accounts->find_account
(get_source_account());
Account* tar_account = accounts->find_account(target_account);
if (src_account == NULL || tar_account == NULL) {
return(1);
}
if (!Transaction::verify_account(src_account)) {
return(1);
}
if (!src_account->check_balance(get_amount())) {
return(1);
}
src_account->modify_balance(-get_amount());
tar_account->modify_balance(get_amount());
return(0);
}
#endif
TransactionList::TransactionList(unsigned sz)
{
transnum = 0;
TransList = new Transaction*[size=sz];
}
TransactionList::~TransactionList()
{
cleanup();
delete TransList;
}
int
TransactionList::add_trans(Transaction* t)
{
if (transnum < size) {
TransList[transnum++] = t;
return(0);
}
return(1);
}
void
TransactionList::cleanup()
{
int i;
for (i=0; i < transnum; i++) {
delete TransList[i];
}
}
void
TransactionList:sprint(FILE* fd)
{
int i;
for (i=0; i < transnum; i++) {
TransList[i]->print{fd);
}
}
|