Sample Assignment Using the Observer Pattern

Revisions: Nov 14, 2007: Added sample code link to to sample code compiled with Visual C++ 2005 Express Edition compiler


In this assignment we will model  BankAccounts, Stocks and containers that use STL-style iterators. We will also model actions on stocks and bank accounts as "trigger" objects that are based on the Observer Programming Pattern. Notice that many details have been left up to you to design.

Part1) The containers
Create a class called OrderedCollection with  the following interface:
int size(void) //answer the number of elements
add(T &e) //add an element to the end of the collection
addFirst(T & e) //add an element to the front of the collection
addLast(T & e) //add an element to the end of the collection
T & removeFirst() //answer and remove the first element
T & removeLast() //answer and remove the last element
remove(T &e) //remove e from the collection if it exists
bool includes(T &e) //answer whether the collection includes e
OrderedIterator<T> & begin()
    //answer an iterator pointing at the first element
OrderedIterator<T> & end()
    //answer an iterator pointing a the last element
 

Create an iterator class OrderedIterator<T> which is a friend of OrderedCollection<T>, and supports the following methods

operator++ //increment the iterator to the next element
operator* //de-reference the iterator to get the element
          //notice iterators work like pointers
bool operator==(OrderedIterator<T> & i)
          //answer whether iterator *this and i refer to the
          //same element
bool operator!=(OrderedIterator<T> & i)
          //answer whether iterator *this and i don't refer to the
          //same element

Example of iterator use

OrderedCollection<BankAccount> s;
//add some bank accounts to s
for (OrderedIterator<BankAccount> i = s.begin(); i != s.end(); i++ )
 cout << *i;



Part 2 Building BankAccounts and Stocks

For this part you need to model  BankAccounts, some Stock objects, and a customer's Portfolio

The BankAccount class should include methods (you can add others)
void withdraw(Float amount)
void deposit(Float amount)
Float balance() //answer the balance

Create a class Stock which represents a holding of a stock on the stock market. For example the stock NASDAQ ENTU 100 $29.23 represents 100 shares of the stock for Entrust Technologies trading on the NASDAQ exchange whose current marked value is $29.23.
A stock should have the following properties ( you can add additional ones if you like): exchange (string representing the stock exchange), symbol (string representing the trading symbol, e.g. "ENTU"), a marketValue which is a float representing the current market value of the stock, and integer shares which is the number of shares of the stock.

Create a class Portfolio that represents a customers trading account and  the stocks they currently own. A Portfolio should have the following properities:
owner (String representing the customer)
portfolioAccountNumber (int representing unique portfolio id)
account (instance of BankAccount from which stock purchases and sales are made)
stocks (an OrderdCollection of the stocks currently owned by the portfolio owner)
actions (an OrderedCollection of current actions pending against accounts or stocks -see below).

A portfolio should include the following methods, but you can add more:
buy(Stock s, int ns)
//purchase ns shares of stock s from account in the portfolio
sell(Stock s, int ns)
//sell ns shares of stock s and deposit the money in account in portfolio
addAction(Action a)
//add an action object to the current actions of the portfolio
removeAction(Action a)
//remove the action a from the portfolio
priceChange(char * symbol, Float price)
  //set the current market value of stock symbol to price
print(ostream &o)
//print the porfolio

A $30.00 service charge should applied to any buy or sell transaction in a portfolio.  Place this service charge in a class variable so it can be adjusted from time to time (probably raised). The portfolio printout should include all the stocks held and the balance of the account and finally the current value of the portfolio. The current value is the balance of the account and the value of each stock owned times the number of shares owned.

Part 3 Stock and Account Actions

Suppose you want to buy a particular stock and sell when, the purchase price rises above a certain amount. Someone could watch the stock to see when the event in question happens.
An alternative is to have an Action object that will watch for the event and then react to it. Such Action objects can be based on the Observer programming pattern (that will be discussed in class).
In this assignment you have to create Action objects that are capable of watching Stock objects. To do so  Stock classes must implement the subject protocol. We will do this by having the Stock class inherit from an abstract class Subject.  Class Action will inherit from abstract class Observer.

Class Subject should have the following methods
attach(Observer * obs) //attach an observer
dettach(Observer * obs) //dettach an observer
notify()  //notify all observers by invoking their update() method

Class Observer should have the following protocol
update(Subject *sub) //react to a change in the subject sub

You can implmement class Action any way you like but it must follow the Observer pattern and inherit from class Observer. If a portfolio customer wants to purchase some stock or sell some stock when it hits a certain value, an Action object should be created and it should attach itself to the stock. Whenever the stock's price changes, the action object should have a chance to react to the change and if necessary sell or buy stock on behalf of the portfolio. (You should purchase stock at current market value using  Action objects as well which do not specify a specific price -you need to decide how to implement this). When an Action object "fires" it should print a message about the details of  the transaction to cout.
 

Testing:
Test your code with a main program and a file that works as follows.
You need a file, that you create, to simulate stock prices. The file should have individual lines that represent the stock symbol and the current market value. For example.
entu 43.00
ibm 53.00
nt 117.00
entu 44.00
ibm 51.00
nt 119.00
entu 47.00
nt 120.00
...

Create a main program that does the following.

create a bank account and deposit $10.000

create a portfollio using the above account

create action objects to purchase some shares of some stocks at market value (purchase $10.000 worth of stock)

create some action objects to buy and sell stocks at a specific values.

read the simulation file

each action object that fires should print out the shares and amount etc.

at the end of the simulation file print out the portfolio, which should show all stocks, number of shares, market value, bank account balance and total portfolio value.

print out a final statement showing profit or loss (based on original bankaccount balance)



  Sample Code:

Sample Code (Visual C++ 2005)


Class OrderedCollection and its Iterator

#include <iostream.h>

template <class T>
class OrderedIterator;

template <class T>
class OrderedCollection {
//    =================
  friend class OrderedIterator<T>;
  const int capacity_;
  int size_; //number of elements
  const T **buffer;

public:
  OrderedCollection(int size = 100) : capacity_(size),
                  buffer(new const T*[size]), size_(0){}

  OrderedCollection(const OrderedCollection<T> & c):
     capacity_(c.capacity_), size_(c.size_), buffer(new const T*[c.capacity_])
  {
   for(int i = 0; i < capacity_; i ++)
    buffer[i] = c.buffer[i];
  }

  ~OrderedCollection() {delete [] buffer; }
 

  typedef OrderedIterator<T> iterator;

  int size() {return size_;}; //answer number of elements
  void add(const T & element);
  void addLast(const T & element);
  void addFirst(const T & element);
  T & removeLast();
  T & removeFirst();
  int remove(const T & element);
  iterator begin(void) {return OrderedIterator<T>(*this);}
  iterator end(void) {return OrderedIterator<T>(*this, size());}
  void printOn(ostream & out) {
   for (int i = 0; i < size_; i++)
    cout << *buffer[i] << "\n";
  }

};

template <class T>
void OrderedCollection<T>::add(const T & element){
 if(size_ < capacity_)
    buffer[size_++] = &element;
}

template <class T>
void OrderedCollection<T>::addLast(const T & element){
 add(&element);
}

template <class T>
void OrderedCollection<T>::addFirst(const T & element){
 if(size_ < capacity_){
    for (int i = size_; i>0; i--)
     buffer[i] = buffer[i-1];
    buffer[0] = &element;
    size_++;
 }
}

template <class T>
T & OrderedCollection<T>::removeLast(){
 return * (T*) (buffer[size_--]);
}

template <class T>
T & OrderedCollection<T>::removeFirst(){
 T* temp = buffer[0];
 for(int i = 0; i<size_ - 1; i++)
  buffer[i] = buffer[i + 1];
 size_--;
 return * (T*) temp;
}

template <class T>
int OrderedCollection<T>::remove(const T & item){
 //remove first occurence of element == item
 //return true if element was found and removed
 int found = 0;
 for(int i = 0; i<size_; i++) {
   if((*buffer[i] == item) && !found) found = 1;
  buffer[i] = buffer[i + found];
 }
 size_ -= found;
 return found;

}

template <class T>
ostream & operator<<(ostream & out, OrderedCollection<T> & collection) {
 collection.printOn(out);
 return out;
}

template <class T>
class OrderedIterator {
//    ===============
 int index;
 OrderedCollection<T> & s;
 public:
  OrderedIterator(OrderedCollection<T> & set, int position = 0) :
    s(set), index(position){}
 T & operator*() {return *(T*)(s.buffer[index]);};
 OrderedIterator<T> & operator++(int) {index++; return *this;};
 bool operator==(OrderedIterator<T> & iter)
 {return (&(this->s) == &(iter.s))&&(this->index == iter.index);};
 bool operator!=(OrderedIterator<T> & iter) {return !(*this == iter);};
};
 



BankAccount Class

#include <iostream.h>
#include <string.h>
 

class BankAccount {
//    ===========
static int nextAccountNumber;
int accountNumber;
float balance_;

public:
 BankAccount(float amount = 0.0):
   balance_(amount), accountNumber(nextAccountNumber++) {}

 void printOn(ostream & out) {
  out << "acct#" <<accountNumber << "  $" << balance_;
 }

    void deposit(float amount) {balance_ += amount;}

 void withdraw(float amount) {balance_ -= amount;}

 float balance() const {return balance_;}
};

ostream & operator<< (ostream & out, BankAccount & b) {
 b.printOn(out);
 return out;
}

int BankAccount::nextAccountNumber = 1000;
 


Observer Class

#include <iostream.h>
#include <string.h>

class Subject;
class Observer {
    public:
  virtual void update(Subject * subject)= 0;

  virtual void printOn(ostream & out) const = 0;

  int operator==(const Observer & obs) const {
    return this == & obs;
  }

};

ostream & operator<<(ostream & out, const Observer & obs) {
 obs.printOn(out);
 return out;
}
 


Subject and Stock Class

#include <iostream.h>
#include <string.h>

class Subject {
//    =======
 OrderedCollection<Observer> observers;
    public:
  void attach(Observer * obs) {
   observers.add(*obs);
  }
  void dettach(Observer * obs) {
   observers.remove(*obs);
  }
  void notify() {

   //Note: observer collection is copied and the copy
   //iterated over so that observers can dettach
   //when they are being notified (i.e. double buffering)

            OrderedCollection<Observer> observers_copy = observers;
   for(OrderedCollection<Observer>::iterator itr = observers_copy.begin();
       itr != observers_copy.end(); itr++) {
      (*itr).update((Subject*) this);
   }
  }
};

class Stock :public Subject {
//    =====
char * exchange; //not used for this assignment
char * symbol;
int shares;
float market_value;

public:
 Stock(char * sym = "ACME", float price = 0.0, int num_shares = 0, char * xch = "NASDAQ" ):
   shares(num_shares), market_value(price) {

      exchange = new char[strlen(xch) + 1];
      strcpy(exchange,xch);
   symbol = new char[strlen(sym) + 1];
      strcpy(symbol,sym);
 }

 Stock (const Stock & s) : shares(s.shares), market_value(s.market_value)
 {
    exchange = new char [strlen(s.exchange) + 1];
    strcpy(exchange, s.exchange);
    symbol = new char [strlen(s.symbol) + 1];
    strcpy(symbol, s.symbol);
 }

 Stock & operator=(const Stock & s)
 {
    if (this != &s) {
     delete [] exchange;
        delete [] symbol;

        market_value = s.market_value;
        shares = s.shares;
        exchange = new char [strlen(s.exchange) + 1];
        strcpy(exchange, s.exchange);
        symbol = new char [strlen(s.symbol) + 1];
        strcpy(symbol, s.symbol);

    }

 return *this;
 }
 

 ~Stock(void) {
    delete exchange;
    delete symbol;
 }
 void printOn(ostream & out) const {
  out << symbol << "  " << shares << " $" << market_value;
 }
 int operator==(const Stock & s) const {
  return strcmp(symbol,s.symbol) == 0;}

 void setMarketValue(float newprice) {
  market_value = newprice;
     notify();
 }
 float getMarketValue() {return market_value;}

 void setShares(int n) { shares = n; }

 int getShares(void) {return shares;}

 char * getSymbol() {return symbol;}

};
ostream & operator<< (ostream & out, const Stock & s) {
 s.printOn(out);
 return out;
}

 


Portfolio Class

#include <iostream.h>
#include <string.h>
 

class Portfolio {
//    =========
char * owner;
static int next_portfolio_id;
static float service_charge;
int portfolio_id;
BankAccount & account;
OrderedCollection<Stock> stocks;
//portfolio assignment not allowed hence private
Portfolio & operator=(const Portfolio & p){}
 

public:
 Portfolio(char * customer, BankAccount & acct):
   account(acct), portfolio_id(next_portfolio_id++) {
    owner = new char [strlen(customer) + 1];
    strcpy(owner, customer);
 }

 Portfolio (const Portfolio & p) : account(p.account), portfolio_id(next_portfolio_id++)
 {
    owner = new char [strlen(p.owner) + 1];
    strcpy(owner, p.owner);
    OrderedCollection<Stock> & p_stocks = (OrderedCollection<Stock>) p.stocks;
    for(OrderedCollection<Stock>::iterator itr = p_stocks.begin(); itr != p_stocks.end(); itr++)
     stocks.add(*itr);
 }

 ~Portfolio(void) {
    delete owner;
 }

 void priceChange(char * stock_symbol, float price) {
    //change the price of any stock with symbol stock_symbol
   for(OrderedCollection<Stock>::iterator itr = stocks.begin(); itr != stocks.end(); itr++)
    if(strcmp((*itr).getSymbol(),stock_symbol)==0) {
     (*itr).setMarketValue(price);

    }

 }

 float market_value() {
    float value = account.balance();
    for(OrderedCollection<Stock>::iterator itr = stocks.begin(); itr != stocks.end(); itr++)
    value += ((*itr).getMarketValue() * (*itr).getShares());
    return value;

 }

 void printOn(ostream & out) {
  out << "Portfolio#" << portfolio_id << "  "<< account << "\n";
  for(OrderedCollection<Stock>::iterator itr = stocks.begin(); itr != stocks.end(); itr++)
         out << *itr << "\n";
  out << "portfolio value $" << market_value() << "\n";
 

 }

 void add(Stock &s) {stocks.add(s);}

 void remove(Stock & s) {stocks.remove(s);}

 void buy(Stock & s, int ns) {
    //buy ns shares of stock s owned by portfolio
    int found = 0;
       for(OrderedCollection<Stock>::iterator itr = stocks.begin(); itr != stocks.end(); itr++)
     if(((*itr) == s) && !found) {
      found = 1;
      account.withdraw((*itr).getMarketValue() * ns);
      account.withdraw(service_charge);
      (*itr).setShares((*itr).getShares() + ns);
      cout << "TRANSACTION: BUY " << ns << " shares of " << s << " portfolio $" << market_value()  << "\n";
      file << "TRANSACTION: BUY " << ns << " shares of " << s << " portfolio $" << market_value()  << "\n";

     }
 }

 void sell(Stock & s, int ns) {
    //sell ns shares of stock s owned by portfolio
    int found = 0;
       for(OrderedCollection<Stock>::iterator itr = stocks.begin(); itr != stocks.end(); itr++)
     if(((*itr) == s) && !found) {
      found = 1;
      account.deposit((*itr).getMarketValue() * ns);
      account.withdraw(service_charge);
      (*itr).setShares((*itr).getShares() - ns);
      cout << "TRANSACTION: SELL " << ns << " shares of " << s << " portfolio $" << market_value() <<"\n";
      file << "TRANSACTION: SELL " << ns << " shares of " << s << " portfolio $" << market_value() <<"\n";

     }
 

 }
};

ostream & operator<< (ostream & out, Portfolio & p) {
 p.printOn(out);
 return out;
}

int Portfolio::next_portfolio_id = 100;
float Portfolio::service_charge = 30.00;

 


Action classes

#include <iostream.h>
#include <string.h>
 

class Action :public Observer {
//    =======
protected:
  Stock & subject;
  Portfolio & portfolio;
  float action_price;
  int num_shares;
public:
 Action(Stock & stock, Portfolio & p, int num, float price = 0.0) :
    subject(stock), portfolio(p), num_shares(num), action_price(price) {
  stock.attach(this);
 }

 virtual void update(Subject * stock) {cout << "Action::update()";}

 virtual void printOn(ostream & out) const {
         out << "Action " << this << "\n";}

};

ostream & operator<<(ostream & out, Action & act) {
 act.printOn(out);
 return out;
}

class BuyAction :public Action {
//    ==========

public:
 BuyAction(Stock & stock, Portfolio & p, int num, float price = 0.0) :
   Action(stock, p, num, price) {}

 virtual void update(Subject * stock){
  if(action_price <= 0.0) {
   portfolio.buy(*(Stock *)stock, num_shares);
   stock->dettach(this);
  }
  else if (((Stock *)stock)->getMarketValue() <= action_price) {
      portfolio.buy(*(Stock *)stock, num_shares);
   stock->dettach(this);

  }

 }
};

class SellAction :public Action {
//    ===========

public:
 SellAction(Stock & stock, Portfolio & p, int num, float price = 0.0) :
     Action(stock, p, num, price) {}

 virtual void update(Subject * stock){
  if(action_price <= 0.0) {
   portfolio.sell(*(Stock *)stock, num_shares);
   stock->dettach(this);
  }
  else if (((Stock *)stock)->getMarketValue() >= action_price) {
      portfolio.sell(*(Stock *)stock, num_shares);
   stock->dettach(this);

  }
 

 }

};

 


Sample main routine

 
#include <stdlib.h>
#include <iostream.h>
#include <fstream.h>

ofstream file ("simulation.txt"); //file to write simulation to

#include "ordcollection.cpp"
#include "bankaccount.cpp"
#include "observer.h"
#include "stock.cpp"
#include "portfolio.cpp"
#include "action.cpp"
 
 

int main() {
 
   BankAccount account;
   Portfolio portfolio("Lou", account);

   account.deposit(10000.00); //opening balance
 
   //create stocks we are interested in
   Stock s1("entu", 43.0);
   Stock s2("nortel", 44.0);
   Stock s3("newbridge", 47.0);
   Stock s4("xerox", 49.0);

   //add stocks we want to trade to portfolio
   portfolio.add(s1);
   portfolio.add(s2);
   portfolio.add(s3);
   portfolio.add(s4);

 
   //print portfolio at start
   file << portfolio << "\n";
 
   //create buy and sell actions to buy and sell stocks
   //when appropriate. actions with price of 0.0 are traded
   //at the current market value of the stock

   BuyAction * buy1 = new BuyAction(s1, portfolio, 20);
   BuyAction * buy2 = new BuyAction(s2, portfolio, 30);
   BuyAction * buy3 = new BuyAction(s3, portfolio, 30);
   BuyAction * buy4 = new BuyAction(s1, portfolio, 20, 18.0 );
   BuyAction * buy5 = new BuyAction(s2, portfolio, 40, 20.0);
   SellAction * sell1 = new SellAction(s1, portfolio, 40, 98.0);
   SellAction * sell2 = new SellAction(s2, portfolio, 40, 75);
 

   //read stock market data simulation file and
   //react to stock price changes

   ifstream infile("stockdata.txt", ios::in);
 if (!infile) {
  cerr << "file could not be opened" << endl;
  exit(1);
 }

   char stockSymbol[80];
   float stockValue;

   while (infile >> stockSymbol >> stockValue) {
  cout << stockSymbol << "  $" << stockValue << "\n";
  file << stockSymbol << "  $" << stockValue << "\n";
  portfolio.priceChange(stockSymbol, stockValue);
 }

   //output final portfolio value
   file << "\n";
   file << portfolio;
 
   infile.close();
   file.close();
   return 0;
}

Sample input file
nortel 55.0
nortel 54.0
nortel 53.0
nortel 15.0
xerox 35.0
newbridge 53.0
nortel 35.0
entu 43.0
entu 15.0
entu 65.0
nortel 75.0
entu 85.0
entu 95.0
nortel 30.0
entu 100.0

 


Sample Output
 

Portfolio#100  acct#1000  $10000
entu  0 $43
nortel  0 $44
newbridge  0 $47
xerox  0 $49
portfolio value $10000

nortel  $55
TRANSACTION: BUY 30 shares of nortel  30 $55 portfolio $9970
nortel  $54
nortel  $53
nortel  $15
TRANSACTION: BUY 40 shares of nortel  70 $15 portfolio $8740
xerox  $35
newbridge  $53
TRANSACTION: BUY 30 shares of newbridge  30 $53 portfolio $8710
nortel  $35
entu  $43
TRANSACTION: BUY 20 shares of entu  20 $43 portfolio $10080
entu  $15
TRANSACTION: BUY 20 shares of entu  40 $15 portfolio $9490
entu  $65
nortel  $75
TRANSACTION: SELL 40 shares of nortel  30 $75 portfolio $14260
entu  $85
entu  $95
nortel  $30
entu  $100
TRANSACTION: SELL 40 shares of entu  0 $100 portfolio $14280

Portfolio#100  acct#1000  $11790
entu  0 $100
nortel  30 $30
newbridge  30 $53
xerox  0 $35
portfolio value $14280