Designing Event Handling Framework using Command Design Pattern

In this article we will learn Command Design Pattern by Designing an Event Handling Framework.

Intent of Command Design Pattern

Encapsulate a request as an object, thereby letting you parametrize clients with different requests, queue or log requests, and support undo able operations.[GoF]

What a heavy Intent 🙂

Lets learn step by step,

Command Design Pattern revolves around Command. It’s main responsibility is to separate out the invoker from the receiver of commands.
To Do this it converts the Command into a Abstract Command Object and maintain a map of REQUEST_KEY and COMMAND Objects. Receiver Defines the Concrete Command Objects and registers them with specific Request Key in this map.

When Invoker receives a Request it fetches the associated Command Object from map and just execute that. Thus Invoker is not aware of Which Command Object is actually receiving that Object.

Command Design Pattern

Also Concrete Command Object which is actually executing that Request is unaware of the fact that who Invoked him.

Now lets see an example. Suppose We are,

Designing a framework which has a capability to listen the USB and Wifi Events from OS

It has an Invoker which reads different Events from OS like,

  • USB has been Plugged
  • USB has been UnPlugged
  • Wifi is Connected
  • Wifi is Disconnected

Invoker knows how to listen for these events from OS and identify them. But it doesn’t know what to do after receiving it because that is application dependent.

So, what Invoker will do after receiving event ?

Invoker has a map of Events and Command Object. For every received Event it will fetch a Command object and call execute on that. But it will not be aware of what’s actually happening inside this Command Object.

Command is actually a Abstract class.


 
class Command
{
public:
	virtual void execute() = 0;
};

The map inside Invoker contains the Concrete Command Objects inside Command Pointer.

So, Invoker is not concerned or aware of what’s actually happening on calling Command Object’s execute. Its main concern is listening the Events and invoking corresponding Command Object. Invoker exposes the interfaces to register Command Objects corresponding to events.

class HardwareEventInvoker 
{
	std::map<EVENTS, std::shared_ptr<Command> > m_registeredCommands;
public:
	/*
	 * This function will be called from framework to handle the event  
	 */
	void handleEvent(EVENTS event)
	{
		auto it = m_registeredCommands.find(event);
		if( it != m_registeredCommands.end())
		{
			std::shared_ptr<Command> cmdPtr = it->second;
			if(cmdPtr)
				cmdPtr->execute();
			
		}
	}
	/*
	 * This function is called by the appication to register its command for Events.  
	 */
	void registerEvent(EVENTS event, std::shared_ptr<Command> cmdPtr)
	{
		m_registeredCommands.insert(std::pair<EVENTS, std::shared_ptr<Command> >(event, cmdPtr));
	}
};

Application that is using our framework contains the Receiver.

Receiver:

In this module, all operations are defined that need to be called on execution of different commands. These operations are defined as Concrete Command Classes representing different Command Objects.

Concrete Command Classes

Let’s see the concrete Command Classes,

class USBConnectedCommand : public Command
{
public:
	void execute()
	{
		std::cout<<"USBConnectedCommand\n";
	}
};
class USBDisconnectedCommand : public Command
{
public:
	void execute()
	{
		std::cout<<"USBDisconnectedCommand\n";
	}
};
class WifiConnectedCommand : public Command
{
public:
	void execute()
	{
		std::cout<<"WifiConnectedCommand\n";
	}
};
class WifiDisconnectedCommand : public Command
{
public:
	void execute()
	{
		std::cout<<"WifiDisconnectedCommand\n";
	}
};

This module creates the Concrete Command Objects and register these Command Objects with Invoker i.e.

HardwareEventInvoker hrdwrEvntInvokerObj;
hrdwrEvntInvokerObj.registerEvent(USB_ATTACHED, std::make_shared<USBConnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(USB_DETTACHED, std::make_shared<USBDisconnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(WIFI_CONNECTED, std::make_shared<WifiConnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(WIFI_DISCONNECTED, std::make_shared<WifiDisconnectedCommand>());

In real environment these Events will be fired from inside the framework when actual hardware action takes place.
But here we will simulate by invoking them manually.
At the time of invocation Invoker don’t know which command is going to execute. Also Receiver of Command i.e. the Concrete Command Object doesn’t how has invoked him.

hrdwrEvntInvokerObj.handleEvent(USB_ATTACHED);
hrdwrEvntInvokerObj.handleEvent(USB_DETTACHED);
hrdwrEvntInvokerObj.handleEvent(WIFI_CONNECTED);
hrdwrEvntInvokerObj.handleEvent(WIFI_DISCONNECTED);

Just like execute() operation we can add undo() operation in Command class hierarchy to achieve the undo capability.

Complete executable code is as follows. To execute this code use following code,

g++ –std=c++11 command.cpp

[code language=”cpp” collapse=”true”]
#include <iostream>
#include <memory>
#include

<map>

/*
* Framework Code Starts
*/
enum EVENTS
{
USB_ATTACHED,
USB_DETTACHED,
WIFI_CONNECTED,
WIFI_DISCONNECTED,

};
class Command
{
public:
virtual void execute() = 0;
};

class HardwareEventInvoker
{
std::map<EVENTS, std::shared_ptr<Command> > m_registeredCommands;
public:
/*
* This function will be called from framework to handle the event
*/
void handleEvent(EVENTS event)
{
auto it = m_registeredCommands.find(event);
if( it != m_registeredCommands.end())
{
std::shared_ptr<Command> cmdPtr = it->second;
if(cmdPtr)
cmdPtr->execute();

}
}
/*
* This function is called by the appication to register its command for Events.
*/
void registerEvent(EVENTS event, std::shared_ptr<Command> cmdPtr)
{
m_registeredCommands.insert(std::pair<EVENTS, std::shared_ptr<Command> >(event, cmdPtr));
}
};

//—————————-
class USBConnectedCommand : public Command
{
public:
void execute()
{
std::cout<<"USBConnectedCommand\n";
}
};
class USBDisconnectedCommand : public Command
{
public:
void execute()
{
std::cout<<"USBDisconnectedCommand\n";
}
};
class WifiConnectedCommand : public Command
{
public:
void execute()
{
std::cout<<"WifiConnectedCommand\n";
}
};
class WifiDisconnectedCommand : public Command
{
public:
void execute()
{
std::cout<<"WifiDisconnectedCommand\n";
}
};

//——————————————————————————//
int main()
{

HardwareEventInvoker hrdwrEvntInvokerObj;
hrdwrEvntInvokerObj.registerEvent(USB_ATTACHED, std::make_shared<USBConnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(USB_DETTACHED, std::make_shared<USBDisconnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(WIFI_CONNECTED, std::make_shared<WifiConnectedCommand>());
hrdwrEvntInvokerObj.registerEvent(WIFI_DISCONNECTED, std::make_shared<WifiDisconnectedCommand>());

// Fire the Events here.
// In real enviornment these Events will be fired from inside the framework when actual
// hardware action takes place. But here we will simulate by invoking them manually.

// At the time of invokation we dont know which command is going to execute.
// So. invoker is completely unaware of actual receiver.

hrdwrEvntInvokerObj.handleEvent(USB_ATTACHED);
hrdwrEvntInvokerObj.handleEvent(USB_DETTACHED);
hrdwrEvntInvokerObj.handleEvent(WIFI_CONNECTED);
hrdwrEvntInvokerObj.handleEvent(WIFI_DISCONNECTED);

return 0;
}

[/code]

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top