State Patterns & C++
by Julian Macri

Listing One
/***** (State class definition) ****************/
class Account;
class State
{
  friend class Account;
  public:
    static State * InitialState(Account * account);
    static double CurrentMinimumBalance()
    {
         /* CODE THAT OBTAINS CURRENT MIN BALANCE FROM SOME DATABASE */
      return CURRENT_MIN_BALANCE;
    };
  private:
    Account * _context;
    double    _balance;
  protected:
    Account * context() const {return _context;};
    void      context(Account * newAccount) {_context = newAccount;};

    double  balance() const {return _balance;};
    void    balance(double newBalance) {_balance = newBalance;};

    virtual State * transitionState() = 0;
  public:
    virtual void deposit(double amount);
    virtual void withdraw(double amount);
    virtual void payInterest() = 0;
  public:
    State(Account * account, double balance)
      : _context(account),
        _balance(balance)
    {};
    State(const State * source)
     : _context(source->context()),
       _balance(source->balance())
    {};
};

Listing Two
/************** (State class method code) ***************/
/************ class State **********************/
State * State::InitialState(Account * account)
{
  return new NonInterestBearingState(account, 0.0);
}
void State::deposit(double amount)
{
  this->balance(this->balance() + amount);
  this->transitionState();
}
void State::withdraw(double amount)
{
  this->balance(this->balance() - amount);
  this->transitionState();
}

Listing Three
/*************** (Concrete state classes) *************/
class InterestBearingState : public State
{
  public:
    static double CurrentRate()
    {
         /* CODE THAT OBTAINS CURRENT RATE FROM SOME DATABASE */
      return CURRENT_RATE;
    };
  protected:
    virtual State * transitionState();
  public:
    virtual void payInterest();
  public:
    InterestBearingState(Account * account, double balance)
      : State(account, balance)
    {};
    InterestBearingState(const State * source)
      : State(source)
    {};
};
class NonInterestBearingState : public State
{
  public:
    static double CurrentTransactionFee()
    {
         /* CODE THAT OBTAINS CURRENT TRANSACTION FEE FROM SOME DATABASE */
      return CURRENT_TRANSACTION_FEE;
    };
  protected:
    virtual State * transitionState();
  public:
    virtual void deposit(double amount);
    virtual void withdraw(double amount);
    virtual void payInterest();
  public:
    NonInterestBearingState(Account * account, double balance)
      : State(account, balance)
    {};
    NonInterestBearingState(const State * source)
      : State(source)
    {};
};
class OverdrawnState : public NonInterestBearingState
{
  protected:
    void sendNoticeToAccountHolder()
    {
         /* PRINT OUT AND MAIL A NOTICE INDICATING ACCOUNT OVERDRAWN */
      cout << "YOUR ACCOUNT IS OVERDRAWN" << endl;
    };
  protected:
    virtual State * transitionState();
  public:
    virtual void withdraw(double amount);
  public:
    OverdrawnState(Account * account, double balance)
      : NonInterestBearingState(account, balance)
    {
      this->sendNoticeToAccountHolder();
    };
    OverdrawnState(const State * source)
      : NonInterestBearingState(source)
    {
      this->sendNoticeToAccountHolder();
    };
};

Listing Four
/*************** (InterestBearingState methods) ********************/
State * InterestBearingState::transitionState()
{
  if (this->context()->balance() < 0)
    this->context()->changeState(new OverdrawnState(this));
  else
    if (this->context()->balance() < State::CurrentMinimumBalance())
      this->context()->changeState(new NonInterestBearingState(this));
  return this->context()->state();
}
void InterestBearingState::payInterest()
{
  this->balance(this->balance() * (1 + this->CurrentRate()));
  this->transitionState();
}

Listing Five
/****************** (NonInterestBearingState methods) *******************/
State * NonInterestBearingState::transitionState()
{
  if (this->context()->balance() < 0)
    this->context()->changeState(new OverdrawnState(this));
  else
    if (this->context()->balance() >= State::CurrentMinimumBalance())
      this->context()->changeState(new InterestBearingState(this));
  return this->context()->state();
}
void NonInterestBearingState::payInterest()
{
      /* PAY NO INTEREST AT ALL */
  cout << "THIS ACCOUNT IS CURRENTLY NOT EARNING INTEREST" << endl;
}
void NonInterestBearingState::deposit(double amount)
{
      /* Charge the transaction fee and then deposit */
  this->balance(this->balance() - this->CurrentTransactionFee());
  this->State::deposit(amount);
  cout << "A TRANSACTION FEE WAS CHARGED" << endl;
}
void NonInterestBearingState::withdraw(double amount)
{
      /* Charge the transaction fee and then withdraw */
  this->balance(this->balance() - this->CurrentTransactionFee());
  this->State::withdraw(amount);
  cout << "A TRANSACTION FEE WAS CHARGED" << endl;
}

Listing Six
/***************** (OverdrawnState methods) **************/
State * OverdrawnState::transitionState()
{
  if (this->context()->balance() >= State::CurrentMinimumBalance())
    this->context()->changeState(new InterestBearingState(this));
  else
    if (this->context()->balance() >= 0)
      this->context()->changeState(new NonInterestBearingState(this));
  return this->context()->state();
}
void OverdrawnState::withdraw(double amount)
{
      /* DO NOT ALLOW THEM TO WITHDRAW MORE MONEY */
  cout << "YOU ARE NOT ALLOWED TO WITHDRAW FROM AN OVERDRAWN ACCOUNT" << endl;
}

Listing Seven
/**************** (Account class definition) **********/
#include "state.h"
class Account
{
  friend class State;
  friend class InterestBearingState;
  friend class NonInterestBearingState;
  friend class OverdrawnState;

  private:
    State * _state;
  protected:
    State *  state() const {return _state;};
    void     state(State * newState) {_state = newState;};
    void changeState(State * newState)
    {
      if (newState != this->state())
      {
        delete this->state();
        this->state(newState);
      }
    };
  public:
    double balance()
    {
      return this->state()->balance();
    };
    void deposit(double amount)
    {
      this->state()->deposit(amount);
    };
    void withdraw(double amount)
    {
      this->state()->withdraw(amount);
    };
    void payInterest()
    {
      this->state()->payInterest();
    };
  public:
    Account()
      : _state(State::InitialState(this))
    {};
    virtual ~Account()
    {
      delete this->state();
    };
};


Listing Eight
/************** (atm.C) ************/
#include "account.h"
#include "iostream.h"
#include "iomanip.h"

int main()
{
  char   option;
  double amount;
  int    quit = 0;

  Account account;
  cout << "WELCOME TO JULIAN'S BANK" << endl;
  cout << "========================" << endl;

  while (!quit)
  {
    cout << endl << "Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, 
                                                     or (q)uit? " << flush;
    cin >> option;
    cout << setiosflags(ios::fixed) << setprecision(2);
    switch (option)
    {
      case 'd' :
      case 'D' :
           cout << "Deposit amount = " << flush;
           cin >> amount;
           account.deposit(amount);
           cout << "Balance = $" << account.balance() << endl;
           break;
      case 'w' :
      case 'W' :
           cout << "Withdrawal amount = " << flush;
           cin >> amount;
           account.withdraw(amount);
           cout << "Balance = $" << account.balance() << endl;
           break;
      case 'i' :
      case 'I' :
           account.payInterest();
           cout << "Balance = $" << account.balance() << endl;
           break;
      case 'q' :
      case 'Q' :
           quit = 1;
           break;
    }
  }
  return 0;
}


Listing Nine
/***************** (Program output) ******************/
[ric1:macri]/u/macri/article/code1 >> atm
WELCOME TO JULIAN'S BANK
========================
Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? d
Deposit amount = 1000
A TRANSACTION FEE WAS CHARGED
Balance = $999.00

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? i
Balance = $1063.94

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? d
Deposit amount = 500
Balance = $1563.94

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? w
Withdrawal amount = 1250
Balance = $313.93

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? i
THIS ACCOUNT IS CURRENTLY NOT EARNING INTEREST
Balance = $313.93

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? w
Withdrawal amount = 500
YOUR ACCOUNT IS OVERDRAWN
A TRANSACTION FEE WAS CHARGED
Balance = $-187.07

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? w
Withdrawal amount = 200

YOU ARE NOT ALLOWED TO WITHDRAW FROM AN OVERDRAWN ACCOUNT
Balance = $-187.07

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? d
Deposit amount = 1000
A TRANSACTION FEE WAS CHARGED
Balance = $811.94

Do you want to (d)eposit,(w)ithdraw, earn (i)nterest, or (q)uit? q
[ric1:macri]/u/macri/article/code1 >>


Listing Ten
/****************** (State class definition) *********************/
class State
{
  public:
    static State * InitialState();
  public:
    virtual int isOverdrawn() = 0;
    int isNotOverdrawn() {return !this->isOverdrawn();};

    virtual int isInterestBearing() = 0;
    int isNotInterestBearing() {return !this->isInterestBearing();};

    virtual int requiresTransactionFee() = 0;
    int doesNotRequireTransactionFee() {return 
                                    !this->requiresTransactionFee();};
};
class InterestBearingState : public State
{
  public:
    virtual int isOverdrawn()            {return 0;};
    virtual int isInterestBearing()      {return 1;};
    virtual int requiresTransactionFee() {return 0;};
};
class NonInterestBearingState : public State
{
  public:
    virtual int isOverdrawn()            {return 0;};
    virtual int isInterestBearing()      {return 0;};
    virtual int requiresTransactionFee() {return 1;};
};
class OverdrawnState : public NonInterestBearingState
{
  public:
    virtual int isOverdrawn() {return 1;};
};

Listing Eleven
/***************** (Account class definition) *****************/
#include "state.h"
#include "iostream.h"

class Account
{
  public:
    static double CurrentRate();
    static double CurrentMinimumBalance();
    static double CurrentTransactionFee();
  protected:
    enum EVENT
    {
      BALANCE_CHANGED,
      STATE_CHANGED
    };
  private:
    double  _balance;
    State * _state;
  protected:
    void event(EVENT event);
    State *   state() const {return _state;};
    void state(State * newState)
    {
      _state = newState;
      this->event(STATE_CHANGED);
    };
    void changeState(State * newState)
    {
      if (newState != this->state())
      {
        delete this->state();
        this->state(newState);
      }
    };
    void balance(double newBalance)
    {
      _balance = newBalance;
      this->event(BALANCE_CHANGED);
    };
    void sendNoticeToAccountHolder()
    {
         /* PRINT OUT AND MAIL A NOTICE INDICATING ACCOUNT OVERDRAWN */
      cout << "YOUR ACCOUNT IS OVERDRAWN" << endl;
    };
  public:
    double balance() { return _balance; };
    void deposit(double amount)
    {
      this->balance(this->balance() + amount);
    };
    void withdraw(double amount)
    {
      if (this->state()->isNotOverdrawn())
        this->balance(this->balance() - amount);
      else
        cout << "YOU ARE NOT ALLOWED TO WITHDRAW FROM 
                                        AN OVERDRAWN ACCOUNT" << endl;
    };
    void payInterest()
    {
      if (this->state()->isInterestBearing())
        this->balance(this->balance() * (1 + Account::CurrentRate()));
      else
        cout << "THIS ACCOUNT IS CURRENTLY NOT EARNING INTEREST" << endl;
    };
  public:
    Account()
      : _state(State::InitialState()),
        _balance(0)
    {};
    virtual ~Account()
    {
      delete this->state();
    };
};

Listing Twelve
/**************** (Account methods) ***************/
#include "account.h"

const double CURRENT_RATE            = 0.065;
const double CURRENT_MIN_BALANCE     = 500.00;
const double CURRENT_TRANSACTION_FEE = 1.00;

double Account::CurrentRate()
{
         /* CODE THAT OBTAINS CURRENT RATE FROM SOME DATABASE */
  return CURRENT_RATE;
}
double Account::CurrentMinimumBalance()
{
         /* CODE THAT OBTAINS CURRENT MIN BALANCE FROM SOME DATABASE */
  return CURRENT_MIN_BALANCE;
}
double Account::CurrentTransactionFee()
{
         /* CODE THAT OBTAINS CURRENT FEE FROM SOME DATABASE */
  return CURRENT_TRANSACTION_FEE;
}
void Account::event(EVENT event)
{
  switch (event)
  {
    case BALANCE_CHANGED :
           if (this->state()->requiresTransactionFee())
           {
                  /* Set the instance variable directly because*/
                  /* we don't want to trigger another event.   */
             _balance -= Account::CurrentTransactionFee();
             cout << "A TRANSACTION FEE WAS CHARGED" << endl;
           }
           if (this->balance() < 0)
           {
             if (this->state()->isNotOverdrawn())
               this->changeState(new OverdrawnState());
           }
           else if (this->balance() < Account::CurrentMinimumBalance())
           {
             if (this->state()->isInterestBearing() ||
                 this->state()->isOverdrawn())
               this->changeState(new NonInterestBearingState());
           }
           else
           {
             if (this->state()->isNotInterestBearing())
               this->changeState(new InterestBearingState());
           }
           break;
    case STATE_CHANGED :
           if (this->state()->isOverdrawn())
             this->sendNoticeToAccountHolder();
           break;
  }
}





9


