Saturday, May 28, 2022

Command Routing Using Chain Of Responsibility Design Pattern...



The Song in English Script:


Jodi tor daak shune keu na aashe tobe ekla cholo re.
Ekla cholo ekla cholo ekla cholo ekla cholo re.
Jodi keu kotha na koy ore ore o obhaga
Jodi shobai thaake mukh phiraye shobai kore bhoy
Tobe poran khuleyO tui mukh phutey tor moner kotha ekla bolo re.
Jodi shobai phirey jay ore ore o obhaga
Jodi gohon pothe jabar kaale keu phirey na chaai
Tobe pother kaantaO tui roktomakha chorontole ekla dolo re.
Jodi aalo na dhore orey orey o obhaga
Jodi jhor badole aandhar raate duyar dey ghore
Tobe bojranole Aapon buker paanjor jwalie niye ekla jolo re.

The Song in English Translation:


If they pay no heed to your call walk on your own.
Walk alone, walk alone, walk alone, walk all alone.
If none speaks, o wretched one,
If all turn their face away and cower in silence—
Then open out your heart
dear one, speak out your mind, voice alone.
If everyone spurns, o wretched one
If all leave you in the lurch in the wilderness
Then trample the trail of thorns
With your blood-stained feet alone, on your own.
If no one holds up a light, o wretched one
And bolts the doors on a dark, stormy night
Then in the lightning fire of pain
Kindle your own heart and keep it burning bright alone.


It takes time to gain expertise…

Today, with just a Google search, we can get a hell of a lot of information about a detailed analysis of any specific software framework. But when I started in the mid ‘90s with MFC/Visual C++, it was an entirely different ball game. So, i struggled, i banged my head, i travelled a long distance in the hot summer, i did everything a normal human being can do to decipher the internal code structure of MFC/VC++. I remember, i started studying the Gang of Four Design Pattern book around 2001 - 2002. And then the engineer in me was again born. The analytical mind to dig into a framework code to understand different aspects of Object oriented Design Principle was again honed.

And hence, when I googled today about the use of Chain Of Responsibility Design pattern in MFC’s Command Routing architecture, I got a hell of a lot of big documents describing all the applicable Gang of Four Design Pattern in just a flash.

And then I remember, in Bangalore/Trivandrum, my everyday night outs in front of an ordinary PC to understand by taking notes and creating an image of the exact flow in the mind to get an uncluttered mind about this very Design pattern in the MFC command routing architecture.

Universe, today let me tell you about the road an inquisitive mind has to travel to decipher a specific maze out of his own. I will tell you my confusion about mapping the design used in the MFC command routing when i started to look at it from a designer’s perspective.

The first thing that, as the name suggests, it has to do something with the command pattern. And, as the basic principle of command pattern is to execute a specific chunk of code, whenever the command is asked to be executed, and obviously this is what is happening here. Hence, i started looking at the whole flow with just keeping Command Pattern in mind. However, somewhat was missing. I spent two three days about that missing part. it was not exactly command pattern. Then what it could have been.

Then suddenly, one early morning,in Trivundrum, i jumped out of bed and looked at the rough sketch of the flow and then it clicked probably, the commands are passed using the Chain Of Responsibilty design pattern, because its absolutely fixed in nature, has to happen only on the path of the chain - no exception.

And voila!!!

After that, it was just a cake walk.

Everything just fell in line.

The difficult code started behaving like bhiggi billi.

No camouflage.

Clear cut.

The intention of the designer became absolutely clear.

And, the scientist in me became happy just to figure it out.

Let’s not forget that a scientist never thinks of just making money to remain happy and contended and motivated.

Otherwise, the ancient sages of #bharat would not have become sages just for acquiring knowledge and wisdom.

For certain people, the calling is higher.

We had Swami Vivekananda, Netaji Subhash Chandra Bose, Rishi Aurobindo and many more who were purposefully erased from the minds of the young generation of #bharat to push a certain political agenda of making us impotent and feeble so that we forget WhoWeAre as a nation.

I remember, after few months, before i left Trivandrum, there was a telephonic interview from Chennai where i told about this basic architecture of MFC command routing and the interviewer was just flattened.

So, my suggestion to all of the learners of Object oriented principles, don’t study Command Design Pattern and Chain of Responsibility as two separate entities.

Rather, think of them as the right jugalbandi where the commands from the Command Pattern will be passed along the chain of the Chain of Responsibility Design pattern, and MFC command routing architecture is a practical framework where this basic principle has been applied.

And, hence, when my currently 11-year-old son #ridit completed his study about the command pattern, I refactored his code to fit into the Chain of Responsibility design pattern, which will be his next area of study.

See, the dots are so nicely connected backwards.

I think I submitted my first article on the use of a specific Design pattern in MFC (i can’t remember which one I did) at CodeGuru with a UML diagram and everything possible to explain it to a layman, and my joy was unbounded when, after a few days, it was published. And then the confidence was built, and the journey to contribute to the learning community started.

So you know, it's hard work, determination, sweat and blood, night outs in front of the computer, and sheer resilience that today I can work as an independent #guru for my currently 11-year-old son #ridit.

So, a few days back, when Ridit completed the Command Design Pattern, and he finished the learning process by offering an online training explaining the same design pattern while programming in C++ in camera, I decided to train him on Chain Of Responsibility as the right application of the Command Pattern, and this idea I took from my initial days of study of the MFC framework.

So, you see, the dots ultimately get connected backwards…

Here's my efforts in which a kind of application where a command will be routed along with the path of a predetermined chain of the Chain Of Responsibility design pattern has been depicted.

Enjoy.

My application, written in C++ showcasing use of the Chain of Responsibility to route a Command

I refactored my son's code of Command Pattern, and here is the result…

/*
 * Level.h
 *
 *  Created on: May 28, 2022
 *      Author: som
 */

#ifndef LEVEL_H_
#define LEVEL_H_

enum class Level {level1, level2, level3};

enum class CommandType {Tea, Coffee};


#endif /* LEVEL_H_ */
#ifndef COMMAND_H_
#define COMMAND_H_

#include <iostream>
#include "Receiver.h"
#include "Level.h"


class Receiver;

using namespace std;

class Task {
protected:
	CommandType type;
	string description;
	Level level;
	Receiver* receiver;

protected:


public:
	Task(CommandType inType, string inDescription, Level inLevel, Receiver* inReceiver);
	virtual ~Task();

	virtual void execute();

	CommandType getCommandType();


	Level getLevel();


	string getDescription();

	Receiver* getReceiver();

};



#endif /* COMMAND_H_ */

#include "Command.h"
#include "Level.h"

Task::Task(CommandType inType, string inDescription, Level inLevel, Receiver* inReceiver){
	type = inType;
	description = inDescription;
	level = inLevel;
	receiver = inReceiver;

}

Task::~Task(){

}

CommandType Task::getCommandType(){
	return type;
}

Level Task::getLevel(){
	return level;
}

string Task::getDescription(){
	return description;
}

Receiver* Task::getReceiver(){
	return receiver;
}

void Task::execute(){
	receiver->handleCommand(this);
}
#ifndef RECIEVER_H_
#define RECIEVER_H_

#include <iostream>

using namespace std;

class Task;
class Receiver {

protected:
	Receiver* successor; //next level
public:

	Receiver();

	Receiver(Receiver* inSuccessor);

	virtual ~Receiver();

	virtual bool handleCommmandImpl(Task* command) = 0;


	void handleCommand(Task* command);

	Receiver* getSuccessor();

};

#endif /* RECIEVER_H_ */

#include "Receiver.h"
#include "Command.h"

Receiver::Receiver(){

	successor = NULL;
}

Receiver::Receiver(Receiver* inSuccessor){

	successor = inSuccessor;

}

Receiver::~Receiver(){

}

void Receiver::handleCommand(Task* command){

	bool handledAtThisNode = this->handleCommmandImpl(command);

	if((successor != NULL) && !handledAtThisNode) {
		successor->handleCommand(command);
	}

}

Receiver* Receiver::getSuccessor(){
	return successor;
}

#ifndef RECEIVERLEVEL1_H_
#define RECEIVERLEVEL1_H_

#include "Receiver.h"

class ReceiverLevel1 : public Receiver{


public:
	ReceiverLevel1(Receiver* inSuccessor);


	virtual ~ReceiverLevel1();

	//override
	bool handleCommmandImpl(Task* command);
};

#endif /* RECEIVERLEVEL1_H_ */

#include "ReceiverLevel1.h"

#include "Command.h"

ReceiverLevel1::ReceiverLevel1(Receiver* inSuccessor){

	Receiver::successor = inSuccessor;

}

ReceiverLevel1::~ReceiverLevel1(){


}
bool ReceiverLevel1::handleCommmandImpl(Task* command){

	if(command->getCommandType() == CommandType::Tea){
		if(command->getLevel() == Level::level1){
			cout<<"TeaCommand is handled at level1"<<endl;
			return true;
		}

		else {
			return false;
		}

	}

	if(command->getCommandType() == CommandType::Coffee){
		if(command->getLevel() == Level::level1){
			cout<<"CoffeeCommand is handled at level1"<<endl;
			return true;
		}
		else {
			return false;
		}
	}

	return false;

}
#ifndef RECEIVERLEVEL2_H_
#define RECEIVERLEVEL2_H_

#include "Receiver.h"

class ReceiverLevel2 :public Receiver{


public:

	ReceiverLevel2(Receiver* inSuccessor);
	virtual ~ReceiverLevel2();

	//override
	bool handleCommmandImpl(Task* command);
};

#endif /* RECEIVERLEVEL2_H_ */

#include "ReceiverLevel2.h"
#include "Command.h"

ReceiverLevel2::ReceiverLevel2(Receiver* inSuccessor){
	// TODO Auto-generated constructor stub
	successor = inSuccessor;

}

ReceiverLevel2::~ReceiverLevel2() {
	// TODO Auto-generated destructor stub
}

bool ReceiverLevel2::handleCommmandImpl(Task* command){

	if(command->getCommandType() == CommandType::Tea){
		if(command->getLevel() == Level::level2){
			cout<<"TeaCommand is handled at level2"<<endl;
			return true;
		}

		else {
			return false;
		}

	}

	if(command->getCommandType() == CommandType::Coffee){
		if(command->getLevel() == Level::level2){
			cout<<"CoffeeCommand is handled at level2"<<endl;
			return true;
		}
		else {
			return false;
		}
	}

	return false;
}
#ifndef RECEIVERLEVEL3_H_
#define RECEIVERLEVEL3_H_


#include "Receiver.h"

class ReceiverLevel3 :public Receiver{

public:

	ReceiverLevel3(Receiver* inSuccessor);
	virtual ~ReceiverLevel3();

	//override
	bool handleCommmandImpl(Task* command);
};

#endif /* RECEIVERLEVEL3_H_ */

#include "ReceiverLevel3.h"
#include "Command.h"

ReceiverLevel3::ReceiverLevel3(Receiver* inSuccessor){
	// TODO Auto-generated constructor stub
	successor = inSuccessor;

}

ReceiverLevel3::~ReceiverLevel3() {
	// TODO Auto-generated destructor stub
}

bool ReceiverLevel3::handleCommmandImpl(Task* command){

	if(command->getCommandType() == CommandType::Tea){
		if(command->getLevel() == Level::level3){
			cout<<"TeaCommand is handled at level3"<<endl;
			return true;
		}

		else {
			return false;
		}

	}

	if(command->getCommandType() == CommandType::Coffee){
		if(command->getLevel() == Level::level3){
			cout<<"CoffeeCommand is handled at level3"<<endl;
			return true;
		}
		else {
			return false;
		}
	}

	return false;
}
/*
 * Main.cpp
 *
 *  Created on: May 28, 2022
 *      Author: som
 */

#include <iostream>
#include "Receiver.h"
#include "ReceiverLevel1.h"
#include "ReceiverLevel2.h"
#include "ReceiverLevel3.h"
#include "Command.h"

class Task;

void invokeCommand(Task command){
	command.execute();
}

int main(){

	Receiver* receiver3 = new ReceiverLevel3(NULL);
	Receiver* receiver2 = new ReceiverLevel2(receiver3);
	Receiver* firstReceiver = new ReceiverLevel1(receiver2);
	Task teaCommand(CommandType::Tea, "tea command",Level::level2, firstReceiver);

	Task coffeCommand(CommandType::Coffee, "Coffee command",Level::level3, firstReceiver);

	invokeCommand(teaCommand);
	invokeCommand(coffeCommand);
}

No comments: