Click here to Skip to main content
15,880,905 members
Articles / Programming Languages / C++

Observing the world - how to build a reuseable implementation of a design pattern

Rate me:
Please Sign up or sign in to vote.
4.58/5 (11 votes)
11 Jun 200111 min read 94K   397   86  
An article about the techniques to pour reusable design (a design pattern) into reusable code with an example of the observer pattern.
#ifndef __WEATHER_FORECAST_H__
#define __WEATHER_FORECAST_H__


#include "tool_observer.h"


////////////////////////////////////////////
// class Strategy
//
// Strategy is a very simple subject. 
//

class Strategy
{
public:
	// Define the type of our "subject". This defines also the interface of 
	// the observers.
	typedef tool::SimpleSubject<> StrategySource;

	// Export part of the "subjects" interface using delegation 
	bool AttachObserver( StrategySource::IObserver* pObserver )
	{
		return m_StrategySource.AttachObserver( pObserver );
	}
	bool DetachObserver( StrategySource::IObserver* pObserver )
	{
		return m_StrategySource.DetachObserver( pObserver );
	}

	void StrategyChanged()
	{
		std::cout << "Strategy::StrategyChanged(), notifying observers >>>" << std::endl;
		// Notify all observers
		m_StrategySource.NotifyObserver();
		std::cout << "<<< Strategy::StrategyChanged(), finished notifying observers" << std::endl;
	}

private:
	
	// Our subject implementation
	StrategySource m_StrategySource;
};


////////////////////////////////////////////
// class TempSens
//
// TempSens is a more sophisticated subject. It implements two subjects for different
// parts of the temperature sensor component: 
//   1: A subject for physical state events, like power failure (TechSource)
//   2: A subject for temperature change events (TempSource)
//
class TempSens
{
public:
	
	// The TechSource subject passes some event type information to the observers.
	// For this we adopt the ARG1 template argument of our SimpleSubject to techevent_t
	// Note that we don't need the OBSERVER_INFO and RETURN template arguments at all.
	// However, because you must pass a type for them, we simply use int.
	enum techevent_t { ePowerFailure, eOtherFailure };
	typedef tool::SimpleSubject< int, int, techevent_t > TechSource;
	
	// The TempSource subject is a more sophisticated subject that uses the OBSERVER_INFO
	// stored with each observer. We use this to implement a threshold value. An observer is
	// only notified if the new temperature value is equal or above the threshold. To implement
	// this we need to override SimpleSubject::NotifyObserver. However, this is not very much work:
	struct TempSource : public tool::SimpleSubject< double, int, double >
	{
		virtual void NotifyObserver( double fNewTemp )
		{
			for( clients_t::iterator i = m_aObservers.begin(); i != m_aObservers.end(); ++i )
				// Call Update only if new Temperature is equal or above the stored threshold
				if( i->Info <= fNewTemp )
					i->pObserver->Update( fNewTemp );
		}
	};

	bool AttachTechObserver( TechSource::IObserver* pObserver )
	{
		return m_TechSource.AttachObserver( pObserver );
	}
	bool DetachTechObserver( TechSource::IObserver* pObserver )
	{
		return m_TechSource.DetachObserver( pObserver );
	}
	bool AttachTempObserver( TempSource::IObserver* pObserver, double fThreshold )
	{
		return m_TempSource.AttachObserver( pObserver, fThreshold );
	}
	bool DetachTempObserver( TempSource::IObserver* pObserver )
	{
		return m_TempSource.DetachObserver( pObserver );
	}

	void SetTemp( double fTemp )
	{
		std::cout << "TempSens::SetTemp(), notifying observers >>>" << std::endl;
		// Notify all observer
		m_TempSource.NotifyObserver( fTemp );
		std::cout << "<<< TempSens::SetTemp(), finished notifying observers" << std::endl;
	}

	void TechEvent( techevent_t event )
	{
		std::cout << "TempSens::TechEvent(), notifying observers >>>" << std::endl;
		// Notify all observer
		m_TechSource.NotifyObserver( event );
		std::cout << "<<< TempSens::TechEvent(), finished notifying observers" << std::endl;
	}

private:
	TechSource	m_TechSource;
	TempSource	m_TempSource;

};

class Technician
{
public:
	Technician()
		: m_pTempSens( NULL )
	{}
	~Technician()
	{
		// Remove ourself from the list of observers
		if( m_pTempSens )
			m_pTempSens->DetachTechObserver( &m_Adapter );
	}

	void SetTempSensor( TempSens* pTempSens )
	{
		// Add ourself as observer to the passed sensor. To be an observer
		// of the passed sensor we have to implement the sensors IObserver interface.
		// However, we use an adapter that implements the interface and delegates calls
		// to our member fn SomethingHappened

		if( m_pTempSens )
			// Remove old observing
			m_pTempSens->DetachTechObserver( &m_Adapter );
		
		m_pTempSens = pTempSens;

		if( m_pTempSens ) {
			m_Adapter.SetUpdate( this, &Technician::SomethingHappened );
			m_pTempSens->AttachTechObserver( &m_Adapter );
		}
	}

private:

	// This is the member function used as callback
	int SomethingHappened( TempSens::techevent_t what )
	{
		std::cout << "Technician::SomethingHappened() :";
		switch( what )
		{
			case TempSens::ePowerFailure:
				std::cout << "Power Failure reported" << std::endl;
				break;
			case TempSens::eOtherFailure:
				std::cout << "Generic Failure reported" << std::endl;
				break;
			default:
				std::cout << "UNKNOWN EVENT!" << std::endl;
				break;
		}

		return 0;	// Return value is ignored
	}

	tool::ObserverAdapter< Technician, TempSens::TechSource > m_Adapter;
	TempSens* m_pTempSens;
};

class HurrDet
{
public:
	HurrDet()
		: m_pTempSens( NULL ), m_pStrategy( NULL )
	{}
	~HurrDet()
	{
		if( m_pTempSens )
			m_pTempSens->DetachTempObserver( &m_TempAdapter );
		if( m_pStrategy )
			m_pStrategy->DetachObserver( &m_StrategyAdapter );
	}
	
	void SetTempSensor( TempSens* pTempSens, double  fThreshold)
	{
		if( m_pTempSens )
			// Remove old observing
			m_pTempSens->DetachTempObserver( &m_TempAdapter );
		
		m_pTempSens = pTempSens;

		if( m_pTempSens ) {
			m_TempAdapter.SetUpdate( this, &HurrDet::OnNewTemp );
			m_pTempSens->AttachTempObserver( &m_TempAdapter, fThreshold );
		}
	}

	void SetStrategy( Strategy* pStrategy )
	{

		if( m_pStrategy )
			// Remove old observing
			m_pStrategy->DetachObserver( &m_StrategyAdapter );
		
		m_pStrategy = pStrategy;

		if( m_pStrategy ) {
			m_StrategyAdapter.SetUpdate( this, &HurrDet::OnNewStrategy );
			m_pStrategy->AttachObserver( &m_StrategyAdapter );
		}
	}

private:

	// Member function used as callback for "temperature changed" events.
	int OnNewTemp( double fNewTemp )
	{
		std::cout << "HurrDet::OnNewTemp( " << fNewTemp << " )" << std::endl;
		return 0;	// Return value is ignored
	}

	// Member function used as callback for "strategy changed" events. Note that
	// Strategy does not use the single parameter that can be passed to the observers.
	// However, because we pass the arguments type as template parameter to ISubject 
	// (and therefore to the derived SimpleSubject class) there must be an argument in
	// the notify fn signature. But we can ignore the value.
	int OnNewStrategy( int )
	{
		// Parameter is ignored
		std::cout << "HurrDet::OnNewStrategy()" << std::endl;
		return 0;	// Return value is ignored
	}

	TempSens* m_pTempSens;
	Strategy* m_pStrategy;
	tool::ObserverAdapter< HurrDet, TempSens::TempSource >		m_TempAdapter;
	tool::ObserverAdapter< HurrDet, Strategy::StrategySource >	m_StrategyAdapter;
};

#endif // __WEATHER_FORECAST_H__

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Germany Germany
Daniel Lohmann (daniel@losoft.de) is Assistant Professor at the Distributed Systems and Operating Systems department at Friedrich-Alexander-University Erlangen-Nuremberg, Germany. His main research topic is the design of a highly customizable and scalable operating system product line for deeply embedded systems using static configuration and aspect-oriented techniques. Before joining Universität Erlangen he worked as a freelance trainer and consultant for NT system programming, advanced C++ programming and OOA/OOD. He is interested in upcoming programming techniques like aspect-oriented programming, generative programming and C++ meta coding and has written some nice and handy tools for Windows NT which you can download at his web site.

Comments and Discussions