Click here to Skip to main content
15,892,768 members
Articles / Programming Languages / Objective C

Applying Observer Pattern in C++ Applications

Rate me:
Please Sign up or sign in to vote.
4.69/5 (27 votes)
8 Jan 2001 158.4K   2.1K   109  
This article explains how to avoid object dependencies using the Observer Pattern with a simple example.
// HRateMon.cpp : implementation of the CHeartRateMonitor class
//
#include "stdafx.h"
#include "HRateMon.h"
#include <stdlib.h>

// List of predefined constants
#define ID_HEART_RATE_METER 0x12345

// Structure for heart rate definition
typedef struct tagHeartRate
{
	INT	nFromAge;
	INT nToAge;
	INT nMinValue;
	INT nThresholdValue;
	INT nMaxValue;
}HEARTRATE, * LPHEARTRATE, **LPPHEARTRATE;

/////////////////////////////////////////////////////////////////////////////
// CHeartRateMonitor message handlers

BEGIN_MESSAGE_MAP(CHeartRateMonitor, CCardioMonitor)
	//{{AFX_MSG_MAP(CTimeMonitor)
		ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CHeartRateMonitor::CHeartRateMonitor( CCardioSubject * pCardioSubject ) : CCardioMonitor( pCardioSubject )
{
	// Step 1 - Initialize class members
	m_nAge		= m_pCardioSubject->GetAge();
	m_csProgram = m_pCardioSubject->GetProgram();
	m_nLevel	= m_pCardioSubject->GetLevel();
	// Step 2 - Based on Age and Program initialize min, threshold and max heartrate value
	InitializeHeartRate();
}

CHeartRateMonitor::~CHeartRateMonitor()
{
}

BOOL CHeartRateMonitor::Update( CSubject * pSubject )
{
	// Step 1 - Make sure the right subject updates the heartrate meter
	if( m_pCardioSubject != pSubject )
	{
		return FALSE;
	}
	// Step 2 - Set heart rate if the program changes
	CString csProgram = m_pCardioSubject->GetProgram();
	if( csProgram != m_csProgram )
	{
		m_csProgram	= csProgram;
		InitializeHeartRate();
	}
	else
	{
		m_csProgram	= csProgram;
	}
	// Step 3 - Get age and level
	m_nAge		= m_pCardioSubject->GetAge();
	m_nLevel	= m_pCardioSubject->GetLevel();
	// Step 4 - Get heart rate, if the meter is running
	if( m_bStarted == TRUE )
	{
		LONG lHeartRate = GetHeartRate();
		CHAR szText[ 512 ];

		INT nPercent = ( INT )( ( ( double ) ( lHeartRate - m_nMinValue ) ) / 
						( double ) ( m_nMaxValue - m_nMinValue ) * 100 );
		// Step 5 - Beep to indicate very low or high heart rate
		if( lHeartRate < m_nMinValue )
		{
			MessageBeep( 0xFFFFFFFF );
			nPercent = 0;
		}
		if( lHeartRate > m_nMaxValue )
		{
			MessageBeep( 0xFFFFFFFF );
			nPercent = 100;
		}
		memset( szText, 0, sizeof( szText ) );
		sprintf( szText, "Heart Rate = %ld (%d%%)", lHeartRate, nPercent );
		m_HeartRateMeter.SetText( szText );
		m_HeartRateMeter.SetPos( nPercent );
	}
	// Step 6 - Update the window with the new information
	Invalidate();
	return TRUE;
}

BOOL CHeartRateMonitor::Create( const RECT & rRect, CWnd * pParentWnd, UINT nID )
{
	// Step 1 - Adjust Rectangle
	CRect WndRect = CRect( rRect.left, rRect.top, rRect.left + 490, rRect.top + 120 );
	CRect ClientRect;

	// Step 2 - Create window
	CWnd::Create( NULL, NULL, WS_VISIBLE | WS_BORDER | WS_CHILD | WS_TABSTOP, 
		WndRect, pParentWnd, nID );

	// Step 3 - Create heart rate meter
	GetClientRect( ClientRect );

	CRect HeartRateMeterRect = CRect( ClientRect.left + 20, ClientRect.top + 60, ClientRect.right - 20, ClientRect.top + 80 );
	m_HeartRateMeter.Create( NULL, "0",  WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 
		HeartRateMeterRect, ( CWnd * ) this, ID_HEART_RATE_METER );
	// Step 4 - Set default display
	CHAR szText[ 512 ];

	memset( szText, 0, sizeof( szText ) );
	sprintf( szText, "Heart Rate = 0 (0%%)" );
	m_HeartRateMeter.SetText( szText );
	return TRUE;
}

void CHeartRateMonitor::OnPaint() 
{
	CRect		ClientRect;
	CRect		DrawRect;
	CRect		TitleRect;
	CHAR		szTitle[ 512 ];
	CHAR		szHeartRateValue[ 512 ];

	CPaintDC dc(this);

	// Step 1 - Set background color and mode
	GetClientRect( ClientRect );
	dc.FillSolidRect( ClientRect, RGB( 255, 255, 0 ) );
	TitleRect = CRect( ClientRect.left, ClientRect.top, ClientRect.right, ClientRect.top + 30 );
	dc.FillSolidRect( TitleRect, RGB( 0, 128, 128 ) );
	dc.SetBkMode( TRANSPARENT );
	
	// Step 2 - Set the title color and text
	dc.SetTextColor( RGB( 255, 255, 255 ) );
	DrawRect = CRect( ClientRect.left, ClientRect.top + 5, ClientRect.right, ClientRect.top + 45 );
	memset( szTitle, 0, sizeof( szTitle ) );
	sprintf( szTitle, "HeartRate Monitor : Program - %s and Level - %d", m_csProgram, m_nLevel );
	dc.DrawText( szTitle, strlen( szTitle ), DrawRect, DT_CENTER | DT_WORDBREAK );

	// Step 3 - Set the min, threshold and max values
	dc.SetTextColor( RGB( 0, 0, 0 ) );
	DrawRect = CRect( ClientRect.left, ClientRect.top + 85, ClientRect.right, ClientRect.top + 125 );
	memset( szHeartRateValue, 0, sizeof( szHeartRateValue ) );
	sprintf( szHeartRateValue, "     Min - %.3d                          Threshold - %.3d                              Max - %.3d", 
		m_nMinValue, m_nThresholdValue, m_nMaxValue );
	dc.DrawText( szHeartRateValue, strlen( szHeartRateValue ), DrawRect, DT_LEFT | DT_WORDBREAK );
}

LONG CHeartRateMonitor::GetHeartRate()
{
	// Step 1 - Generate random no. to get the heartrate
	LONG lHeartRate = m_nMinValue + ( rand() % ( m_nMaxValue - m_nMinValue ) );
	// Step 2 - Adjust the heart rate, to simulate the extreme conditions
	// when heart rate falls in that area
	if( lHeartRate - m_nMinValue < 5 )
	{
		lHeartRate -= rand() % 5;
	}
	if( m_nMaxValue - lHeartRate < 5 )
	{
		lHeartRate += rand() % 5;
	}
	return lHeartRate;
}

VOID CHeartRateMonitor::Stop()
{
	// Step 1 - Set default display
	CHAR szText[ 512 ];

	memset( szText, 0, sizeof( szText ) );
	sprintf( szText, "Heart Rate = 0 (0%%)" );
	m_HeartRateMeter.SetText( szText );
	m_HeartRateMeter.SetPos( 0 );
	// Step 2 - Set m_bStarted to FALSE
	m_bStarted = FALSE;
}

BOOL CHeartRateMonitor::InitializeHeartRate()
{
	// Assign some arbitary heart rate
	static HEARTRATE sbFatburn[ 7 ] =
	{
		{ 10, 20, 105, 110, 115 },
		{ 21, 30, 110, 115, 120 },
		{ 31, 40, 115, 120, 125 },
		{ 41, 50, 120, 125, 130 },
		{ 51, 60, 115, 120, 125 },
		{ 61, 70, 110, 115, 120 },
		{ 71, 100, 105, 110, 115 }
	};

	static HEARTRATE sbCardio[ 7 ] = 
	{
		{ 10, 20, 115, 120, 125 },
		{ 21, 30, 120, 125, 130 },
		{ 31, 40, 125, 130, 135 },
		{ 41, 50, 130, 135, 140 },
		{ 51, 60, 125, 130, 135 },
		{ 61, 70, 120, 125, 130 },
		{ 71, 100, 115, 120, 125 }
	};
	INT nIndex = 0;

	// Step 1 - Assign min, max and threshold heart rates based on 
	// the program
	if( m_csProgram.CompareNoCase( "Fatburn" ) == 0 )
	{
		for( nIndex = 0; nIndex < 7; nIndex++ )
		{
			if( m_nAge >= sbFatburn[ nIndex ].nFromAge  && 
				m_nAge <= sbFatburn[ nIndex ].nToAge )
			{
				m_nMinValue			= sbFatburn[ nIndex ].nMinValue;
				m_nThresholdValue	= sbFatburn[ nIndex ].nThresholdValue;
				m_nMaxValue			= sbFatburn[ nIndex ].nMaxValue;
				break;
			}
		}
	}
	else
	{
		for( nIndex = 0; nIndex < 7; nIndex++ )
		{
			if( m_nAge >= sbCardio[ nIndex ].nFromAge  && 
				m_nAge <= sbCardio[ nIndex ].nToAge )
			{
				m_nMinValue			= sbCardio[ nIndex ].nMinValue;
				m_nThresholdValue	= sbCardio[ nIndex ].nThresholdValue;
				m_nMaxValue			= sbCardio[ nIndex ].nMaxValue;
				break;
			}
		}
	}
	return TRUE;
}

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.


Written By
Switzerland Switzerland
Kulathu Sarma is working as a Technology Manager for GoldAvenue, a company based in Geneva, Switzerland and responsible for supporting e-business initiatives of GoldAvenue in B2B and B2C Sectors. He has been programming in C/C++ for 9 years and actively working on Patterns for the past 5 years.

Comments and Discussions