Notifying

Notification requires a notifier and at least one observer. Your code must instantiate these objects:

  1. Derive a notifier from IStandardNotifier..
  2. Derive an observer from IObserver.

In the notifier, you must decide what interests you will allow observers to register for. Then, for each interest, you must perform the following steps:

  1. Build an object of type IInterest.
  2. In the notifier's constructor, call enableNotification().
  3. In the setter for the observed object, set the value and then notify observers of change.

In the observer, for each interest you wish to observe, you must perform the following steps:

  1. Build an IObserverConnectionTo object attached to the notifier corresponding to the interest.
  2. In the constructor for the observer, register the connections.
  3. In the destructor, be sure to unregister the connection.

Basic Synchronous Notification

The following sample demonstrates the use of interest-based, synchronous notification for nonframework components. That is, the default notifier and observer protocols of IModel and IModelView are not assumed.

Note: For an example of implementing notification for components, see the Model/View/Presenter Notification task.

myCustomer is an example of deriving from an IStandardNotifier. myCustomer encapsulates a name, address, and home phone. It supports getting and setting its fields and notifies any registered observers when the name, address, or home phone changes.

//Notification header files
#include <istdntfy.hpp>
#include <iobserv.hpp>
#include <inotifev.hpp

class myCustomer : public IStandardNotifier
{
public:
/** 
* The constructor creates the three supported interests and enables
* notification. Any observer interested in a name change,
* address change, or home phone change will register with these 
* interests.
*/

        myCustomer() 
                :fNameChangedInterest     (*this, nameId),
                fAddressChangedInterest (*this, addressId),
                fHomePhoneChangedInterest(*this, homePhoneId)
                {
                        enableNotification();
                }

        virtual ~myCustomer() {};
        virtual IString name() {return fName;}
        virtual IString address() {return fAddress;}
        virtual IString homePhone() {return fHomePhone;}
        
/**
* These are the setters for myCustomer. The setters will make the 
* requested change and then notify observers by sending the 
* appropriate interest via an INotificationEvent.
*/
        virtual void    setName(IString newname)
        {
                fName = newname;
                notifyObservers(INotificationEvent(nameChangedInterest()));
        }

        virtual void    setAddress (IString newaddress) 
        {
                fAddress = newaddress;
                notifyObservers(INotificationEvent(addressChangedInterest()));
        }

        virtual void    setHomePhone(IString newphone)
        {
                fHomePhone = newphone;
                notifyObservers(INotificationEvent(homePhoneChangedInterest()));
        }
/** 
* These are the getters for the three supported interest. Any observer
* interested in a name change, address change, or home phone change
* will register with these interests.
/*
        IInterest& nameChangedInterest () 
        {
                return fNameChangedInterest;
        }

        IInterest& addressChangedInterest () 
        {
                return fAddressChangedInterest;
        }

        IInterest& homePhoneChangedInterest () 
        {
                return fHomePhoneChangedInterest;
        }    
        private:
        //Our data members
                IString fName;
                IString fAddress;
                IString fHomePhone;
        /**
        * These are interest members corresponding to the observable 
        * data members
        */
                IInterest fNameChangedInterest;
                IInterest fAddressChangedInterest;
                IInterest fHomePhoneChangedInterest;

        //Declare the NotificationIds 
                static INotificationId const nameId, addressId, homePhoneId;
        };

        //Define the INotificationIds
                const INotificationId IC_EXPORTB myCustomer::nameId 
                        = "myCustomer::name";
                const INotificationId  IC_EXPORTB myCustomer::addressId
                        = "myCustomer::address";
                const INotificationId IC_EXPORTB myCustomer::homePhoneId 
                        = "myCustomer::homePhone";

myObserver is a class that receives notifications. It uses IObserverConnections to direct notifications to specific methods. In this example, it receives notifications from a customer object.

class myObserver
{
public:

/** 
* constructor for observer
*/
        myObserver (myCustomer& aCustomer);
        virtual ~myObserver();

/** 
* handler method to handle any change in the customer
*/
        virtual void handleAnyChange (const INotificationEvent& anEvent)
        {
                printf (">> Why hello, myObserver::handleAnyChange just 
                        received                the event: %s \n", anEvent.notificationId());
        }

/** 
* handler method to handle just name changes in the customer
*/
        virtual void  handleNameChange (const INotificationEvent& anEvent)
        {
                printf (">> Why hello, myObserver::handleNameChange just
                        received                the event: %s \n", anEvent.notificationId());
        }

private:
// for each event you are interested in, create a connection object
        IObserverConnectionTo<myObserver> fAnyChangeConnection;
        IObserverConnectionTo<myObserver> fNameChangeConnection;
        myCustomer& fCustomer;
};
/** 
* connect this observer to the given customer notifier
*/
myObserver::myObserver(myCustomer& aCustomer) : 
        fCustomer(aCustomer),
	//all events trigger this method
        fAnyChangeConnection (*this, myObserver::handleAnyChange),
	//only name change events trigger this method
        fNameChangeConnection (*this, myObserver::handleNameChange)
{
        //this connection handles all notifications from aNotifier
        fAnyChangeConnection.handleNotificationsFor(fCustomer);
	//this connection handles only name change notifications from aNotifier
        fNameChangeConnection.handleNotificationsFor
                (fCustomer.nameChangedInterest());
}
/**
* unregister from receiving events from the customer
*/
myObserver::~myObserver()
{
        fAnyChangeConnection.stopHandlingNotificationsFor(fCustomer);
        fNameChangeConnection.stopHandlingNotificationsFor(fCustomer);
}
int main()
{
        myCustomer theCustomer;
        myObserver theObserver(theCustomer);
//should trigger handleAnyChange/handleNameChange
        theCustomer.setName("Bubba Malone");
//should only trigger handleAnyChange
        theCustomer.setAddress("1999 IBM Way");
}

Here is the output from the program:

>> Why hello, myObserver::handleAnyChange just received the event:
                myCustomer::name 
>> Why hello, myObserver::handleNameChange just received the event:
                myCustomer::name 
>> Why hello, myObserver::handleAnyChange just received the event:
                myCustomer::address

 

Notifying Asynchronously

To notify asynchronously, follow the same steps as for synchronous notification, substituting a call to notifyObserversAsync() for notifyObservers(). You can implement truly asynchronous notification by putting the notifier and the observer in different threads.

Passing Data Along With the Event Notification

To pass event data along with a notification of the event, follow the same steps for basic notification, except:

notifyObservers(INotificationEventFor<MyEventDataClass>& event, myAlertInterestFunction(), new myEventDataClass());

for this version:

notifyObservers(myAlertInterestFunction());

IObserverForConnectionTo<myObserver> myDataChangeConnection;

for this template:

IObserverConnectionTo<myObserver> myDataChangeConnection;