View Single Post
  #8 (permalink)  
Old 10-26-2007, 12:07 PM
kkelly's Avatar   
kkelly kkelly is offline
Learning Programmer
 
Join Date: Sep 2007
Posts: 50
Rep Power: 5
kkelly is on a distinguished road
Default

Sure. I wrote this for a scenario that includes a button. When the button is pressed, it raises an event.

The abstract class is called IButtonEvents. It is the interface between the Button class and, in this instance, the SomeEventHandler class. Here is the header for IButtonEvents:
Code:
#pragma once

class IButtonEvents
{
public:

	virtual ~IButtonEvents();			//	Virtual Destructor.

	//	Purpose:	Function declaration for function thats represents an event.  Must be overridden in
	//		a derived class.
	//---------------------------------------------------------------------------------------------------
	virtual void _OnPress() = 0;		//	Is raised when a button is pressed.
};
Since I used a virtual destructor, I must provide implementation for it. Here is IButtonEvents.cpp:
Code:
#include "IButtonEvents.h"

IButtonEvents::~IButtonEvents()
{
}
I just created a class called SomeEventHandler that inherits the interface, overrides the _OnPress function and provides implementation. Here is the header for SomeEventHandler:
Code:
#pragma once
#include <iostream>
#include "IButtonEvents.h"

using namespace std;

class SomeEventHandler : public IButtonEvents
{
private:
	void _OnPress();			//	The overidden function of the inherited virtual function.
					//	AKA, the EventHandler.
};
The implementation for _OnPress just cout's the memory address of the object and identifies the function. Here is the source for SomeEventHandler:
Code:
#include "SomeEventHandler.h"

void SomeEventHandler::_OnPress()
{
	//	Display a response message identifying the object and the event.
	cout << "Object at " << this << " responding to _OnPress event." << endl;
}
For the Button class, I used a linked list of pointers, like I was talking about in the previous post. You can ignore that code. The only thing worth noting is the function AddEventHandler. Notice that an implicit conversion takes place in the parameters. Here is the header for the Button class:
Code:
#pragma once
#include "IButtonEvents.h"

class Button
{
private:

	struct EventHandlerRecord				//	The record structure for the table of EventHandlers
	{
		IButtonEvents * Object;			//	Pointer to an EventHandling object
		EventHandlerRecord * NextRecord;		//	Pointer to the next record
	};

	EventHandlerRecord * FirstRecord;			//	The pointer to the first record
	EventHandlerRecord * LastRecord;			//	The pointer to the last record

public:

	Button::Button();					//	Default Constructor
	Button::~Button();					//	Default Destructor

	//	Parameters:	A pointer to an object derived from the IButtonEvent class.
	//	Purpose:		Adds a record the EventHandler table.
	//-----------------------------------------------------------------------------------
	void AddEventHandler(IButtonEvents * EventHandlerObject);

	//	Purpose:		Raises the _OnPress event function
	//-----------------------------------------------------------------------------------
	void Press();
};
There isn't much worth noting in the source for this class, just that it calls the _OnPress function from the IButtonEvents class. Here is the source for the Button class:
Code:
#include "Button.h"

Button::Button()
{
	/*	===Initialize pointers=== */
	Button::FirstRecord = 0;
	Button::LastRecord = 0;
}
Button::~Button()
{
	/*	===Destroy the EventHandler table=== */
	
	//	create a cursor
	Button::EventHandlerRecord * cursor;

	//	while a record exists . . .
	while (Button::FirstRecord != 0)
	{
		//	. . . point the cursor to the first record
		cursor = Button::FirstRecord;
		//	move the FirstRecord to the next record
		Button::FirstRecord = Button::FirstRecord->NextRecord;
		//	delete the record at the cursor
		delete cursor;
	}
}
void Button::AddEventHandler(IButtonEvents * EventHandlerObject)
{
	//	Create a cursor
	Button::EventHandlerRecord * cursor;

	//	Create a new record
	cursor = new Button::EventHandlerRecord;
	//	Set the Object field
	cursor->Object = EventHandlerObject;

	//	If the first record hasn't been assigned yet . . .
	if (FirstRecord == 0)
	{
		//	. . . make the new record the first record
		Button::FirstRecord = cursor;
	}
	//	Else . . .
	else
	{
		//	. . . append the record to the last record
		Button::LastRecord->NextRecord = cursor;
	}
	//	Make the new record the last record
		cursor->NextRecord = 0;
		Button::LastRecord = cursor;
}
void Button::Press()
{
	/*	===Call the _OnPress function for each object in the EventHandler table===  */

	//	Create a cursor
	Button::EventHandlerRecord * cursor;

	//	Start at the first record
	cursor = Button::FirstRecord;
	//	While the cursor points to a record . . .
	while (cursor != 0)
	{
		//	. . . Call the _OnPress function for that object
		cursor->Object->_OnPress();
		//	Move to the next record
		cursor = cursor->NextRecord;
	}
}
Finally, here is main.cpp:
Code:
#include <iostream>
#include "Button.h"
#include "SomeEventHandler.h"

using namespace std;

int main()
{	
	Button myButton;							//	Button object

	SomeEventHandler * EventHandler1 = new SomeEventHandler;		//	An event handler object
	SomeEventHandler * EventHandler2 = new SomeEventHandler;		//	Another event handler object

	//	Add the EventHandlerObjects to myButton's EventHandler table
	myButton.AddEventHandler(EventHandler1);
	myButton.AddEventHandler(EventHandler2);

	//	Press the button
	myButton.Press();
	
	//	Wait for the new line character
	cin.get();

	//	Destroy the EventHandler objects
	delete EventHandler2;
	delete EventHandler1;

	return 0;
}
That's it. I believe it is a fail-safe design, excluding multiple inheritance. To support aysnchronous calls wouldn't take much work. The only drawback is that an EventHandling class has to inherit the interface, which means they must be designed to handle the events, and can't be bound during run-time. Another layer of abstraction could solve that.
Reply With Quote