Sunday, February 14, 2010

Observer pattern

The Observer Pattern is classified under Object Behavioral Patterns in the book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al. (Addison-Wesley, 1995). In this article, I will be using the terms used by 'Gang of Four (GoF)' to explain Observer Pattern. First, let us understand the problem of object interaction.

GoF classifies the Observer Pattern as an Object Behavioral Pattern. The Observer Pattern is intended to "Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically". An object that is subjected to change is called a Subject and an object that depends on the Subject's state is called an Observer. In the above example, 'B' can be a Subject and Objects A and C can be Observers. A Subject can have any number of Observers. When the Subject changes its state, it Notifies all the Observers and the Observers query the Subject to maintain state consistency with the Subject.

Here is the code.

#include <iostream>
#include <list>
#include <algorithm>



/*
 * Code for the observer pattern
 *
 */


// fwd declare the classes
class Observer;
class Observable;

// Derive your class from this to
// get notifications in DataChanged
class Observer
{
    Observable *m_Observing;

public:
    Observer();
    virtual ~Observer();
    void AttachToObservable(Observable *);
    void UnhookFromObservable( );
    virtual void DataChanged( Observable * );

};

// Derive your class from Observable and call Notify
// whenever the data changes
class Observable
{
    typedef std::list>Observer *< ObserverList;
    typedef ObserverList::iterator ObserverList_Iter;

    ObserverList  m_Observers;

public:
    Observable();
    virtual ~Observable();
    void AddObserver(Observer *);
    void RemoveObserver(Observer *);
    void Notify();
};


Observable::Observable()
{
}

Observable::~Observable()
{
    ObserverList_Iter aIter;
    for( aIter = m_Observers.begin(); aIter != m_Observers.end();++aIter)
    {
        (**aIter).UnhookFromObservable();
    }
}

void Observable::AddObserver(Observer *o)
{
    ObserverList_Iter aIter;

    aIter = std::find( m_Observers.begin(), m_Observers.end(), o);
    if( aIter == m_Observers.end())
    {
        m_Observers.push_back( o);
    }
}


/*
 * This is called from the observer. We find the
 * observer in the list and remove the item.
 *
 */

void Observable::RemoveObserver(Observer *o)
{
    ObserverList_Iter aIter;

    aIter = std::find( m_Observers.begin(), m_Observers.end(), o);
    if( aIter != m_Observers.end())
    {
        m_Observers.erase( aIter );
    }
}


/*
 * Observable::Notify. Notify all the observers that
 * the data has changed.
 *
 */

void Observable::Notify()
{
    ObserverList_Iter aIter;

    for( aIter = m_Observers.begin(); aIter!= m_Observers.end(); ++aIter)
    {
        (**aIter).DataChanged( this );
    }
}

// ===============================================

Observer::Observer()
: m_Observing( NULL )
{
}

Observer::~Observer()
{
    // We are being destroyed. Make sure
    // we remove ourselves from the 
    // observer we attached ourself to.
    UnhookFromObservable();
}

void Observer::UnhookFromObservable()
{
    if( m_Observing )
    {
        m_Observing->RemoveObserver(this);
    }
    m_Observing = NULL;
}

void Observer::DataChanged(Observable*)
{
    std::cout << "Hey, you should override DataChanged in your derived class !!\n";
}

/*
 * In my line of work we price bonds off 
 * benchmark bonds. If the price of the 
 * benchmark bond changes, the price of all bonds
 * that are priced off that benchmark must be 
 * adjusted accordingly.
 *
 * Here is a simple class, Instrument that 
 * can be used for both the 
 * benchmark and the dependent bond. 
 *
 */
class Instrument : public Observable, public Observer
{
    double m_Price;
    double m_Spread;
    std::string m_ID;

public:
    Instrument( std::string & inID)
    : m_ID(inID), m_Price(0.0), m_Spread(10.0)
    {
    }
    Instrument(const char *p)
    : m_ID(p), m_Price(0.0)
    {
    }

    void PriceChanged( double inPrice )
    {
        m_Price = inPrice;
        Notify();
    }
    void DataChanged( Observable *o)
    {
        Instrument *i = dynamic_cast(o);
        if(i)
        {
            std::cout << m_ID << " price updated from " << i->m_ID << "\n";
            this->m_Price = i-<m_Price + m_Spread;
        }
    }
};


void test1()
{
    Instrument  i1( "FGBLZ0");
    Instrument  i2( "DBS bank 2010");

    i1.AddObserver( &i2 );
    i1.PriceChanged( 100);

}


int main(int, char **)
{
    std::cout << "Hello, world!\n";

    test1();

    return 0;
}

// EOF

No comments:

Post a Comment