Friday, May 6, 2022

The State Design pattern in C++ using timer and notification






As a #guru, let me today pay my tributes to my #guru in the tech world who has influenced my technical attributes and mindset.

Although I have never seen them, neither they know I exist.

But I am grateful to them - and because of them I have at last become an engineer in the true sense.

First guru: The authors of The Gang of Four Design Pattern Book

Second Guru: Linus Torvalds for offering Linux and rather helping me come out of the vicious cycle of windows

Third Guru: The Android creator and Google for showing me the different nitty-gritty of a complicated framework that i learnt through delving into the Android framework code

Fourth Guru: A professor of USA University by the name Doug from whom i got insights of how to study properly the Android OS difficult parts of the code like Asynctask Framework and he showed  me the right approach of taking the OS study from en engineer’s perspective - delving into difficult concurrent framework of the Android OS

And my all-time Guru : My family for giving me a sweet, caring, loving #home and my only son, #ridit, for becoming my best student from his early childhood. He is still driving me to burn the midnight oil in front of the laptop to explore the deep ocean of Computer science.

And I am sure, the way the whole professional world of technology is moving, very soon somebody will have to dismiss the book called

#theworldisflat  - The World Is Flat

and write a new one 

#theflatistheworld - The Flat Is The World

Now let's come back to the subject. Let's discuss the technical side of the State Design Pattern.

The state design pattern is a powerful tool in software design, allowing you to encapsulate the behavior of an object based on its internal state.

The main participants in this design pattern are:

Context: This object represents the entire entity experiencing state changes. It holds a reference to the current state object and exposes an interface for external interaction.

State: These are concrete classes representing each possible state the object can be in. Each state class defines its own behavior for responding to events and transitions.

Transitions: These define how the object can move from one state to another. They can be triggered by events, user actions, or internal conditions.

In my example, I have cited the process of cooking chicken - how the chicken states alter from uncleaned state to cleaned state to marinated state to cooked state and finally served to the guests.

The source code is as follows:

/*
* Callback.h
*
*  Created on: May 5, 2022
*      Author: som
*/

#ifndef CALLBACK_H_
#define CALLBACK_H_

class Callback{
public:
virtual void goToNextStateFromCallback() = 0;
virtual ~Callback(){
}
};
#endif /* CALLBACK_H_ */



#ifndef CHICKENSTATE_H_
#define CHICKENSTATE_H_

#include <iostream>

#include <cstring>

#include "Mamma.h"

#include "timer.h"

using namespace std;

class Mamma;

class ChickenState {

public:

ChickenState(){

}

virtual ~ChickenState(){
cout<<"aab chicken khatam... sablog khaa liya"<<endl;
}
virtual void wash(int periodForDisplayMessage , int theTotalTimeNeeded)= 0;
virtual void marinate(int periodForDisplayMessage, int theTotalTimeNeeded ) = 0;
virtual void cook(int periodForDisplayMessage , int theTotalTimeNeeded )= 0;
virtual void serve(int periodForDisplayMessage, int theTotalTimeNeeded ) = 0;
virtual void setMamma() = 0;

virtual void goToNextState() = 0;

virtual char* getTag() = 0;
};

#endif



#ifndef UNCLEANEDSTATE_H_
#define UNCLEANEDSTATE_H_

#include "Mamma.h"
#include "CleanedState.h"
#include "Callback.h"

class UncleanedState: public ChickenState /*, public Callback*/{

private:
Mamma* mamma;
char* tag;

public:

UncleanedState(Mamma* inMamma){

tag = new char[40];
mamma = inMamma;
strcpy(tag , "Uncleaned");
}

//Override
void setMamma(){
//mamma = ChickenState::getMamma();
}

virtual ~UncleanedState(){
delete[] tag;
}

void wash(int periodForDisplayMessage , int theTotalTimeNeeded ){
cout<<"The cooking process is starting... enjoy..."<<endl;
cout<<"chicken is getting washed.... please wait..."<<endl;
/*(mamma->t).setInterval([&]() {
cout<<"chicken is getting washed.... please wait..."<<endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout([&]() {
goToNextState();
}, theTotalTimeNeeded);

}
void marinate(int periodForDisplayMessage , int theTotalTimeNeeded ){

}
void cook(int periodForDisplayMessage , int theTotalTimeNeeded ){

}
void serve(int periodForDisplayMessage , int theTotalTimeNeeded ){

}

//override
void goToNextState(){

mamma->isUncleanedStateDone = true;


//will callback the methods defined in the Mamma class...
(mamma->t).stop();
}


//override
char* getTag(){
return tag;
}
};

#endif




#ifndef CLEANEDSTATE_H_
#define CLEANEDSTATE_H_

#include "Mamma.h"
#include "MarinatedState.h"
//#include "Callback.h"

class CleanedState: public ChickenState /*, public Callback*/{
private:
Mamma* mamma;
char * tag;

public:
CleanedState(Mamma* inMamma){
tag = new char[40];
mamma = inMamma;
strcpy(tag , "Cleaned");
}

virtual ~CleanedState(){
delete[] tag;
}

//Override
void setMamma(){
//mamma = ChickenState::getMamma();
}


void wash(int periodForDisplayMessage , int theTotalTimeNeeded ){
}

void marinate(int periodForDisplayMessage , int theTotalTimeNeeded){
cout<<"chicken is getting marinated.... please wait..."<<endl;
//mamma->timerRunning = true;
/*(mamma->t).setInterval([&]() {
cout<<"chicken is getting marinated.... please wait..."<<endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout([&]() {
goToNextState();
}, theTotalTimeNeeded);

}
void cook(int periodForDisplayMessage , int theTotalTimeNeeded){

}
void serve(int periodForDisplayMessage , int theTotalTimeNeeded){

}

void goToNextState(){
mamma->isCleanedStateDone = true;
(mamma->t).stop();
}

//override
char* getTag(){
return tag;
}

};

#endif





#ifndef MARINATEDSTATE_H_
#define MARINATEDSTATE_H_

#include "ChickenState.h"
#include "CookedState.h"
//#include "Callback.h"


class MarinatedState: public ChickenState/* , public Callback*/{

private:
Mamma* mamma;
char* tag;
public:

MarinatedState(Mamma* inMamma){
tag = new char[40];
mamma = inMamma;
strcpy(tag ,"Marinated");
}

virtual ~MarinatedState(){
delete[] tag;

}

//Override
void setMamma(){
//mamma = ChickenState::getMamma();
}


void wash(int periodForDisplayMessage, int theTotalTimeNeeded ){

}
void marinate(int periodForDisplayMessage, int theTotalTimeNeeded ){

}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded ){

cout<<"chicken is getting cooked... please wait..."<<endl;

/*(mamma->t).setInterval([&]() {
cout<<"chicken is getting cooked... please wait..."<<endl;
}, periodForDisplayMessage);
*/
(mamma->t).setTimeout([&]() {
goToNextState();
}, theTotalTimeNeeded);




}
void serve(int periodForDisplayMessage, int theTotalTimeNeeded ){

}

void goToNextState(){
mamma->isMarionatedStatedone = true;
(mamma->t).stop();
}

//override
char* getTag(){
return tag;
}

};

#endif



#ifndef COOKEDSTATE_H_
#define COOKEDSTATE_H_

#include "Mamma.h"
class CookedState: public ChickenState /*, public Callback*/{
private:
Mamma* mamma;
char* tag;

public:

//Timer t;

CookedState(Mamma* inMamma){
tag = new char[40];
mamma = inMamma;
strcpy(tag ,"Cooked");
}

virtual ~CookedState(){

delete[] tag;

}

//Override
void setMamma(){
//mamma = ChickenState::getMamma();
}


void wash(int periodForDisplayMessage, int theTotalTimeNeeded  ){

}
void marinate(int periodForDisplayMessage , int theTotalTimeNeeded ){

}
void cook(int periodForDisplayMessage, int theTotalTimeNeeded){

}

void serve(int periodForDisplayMessage , int theTotalTimeNeeded){
cout<<"chicken is ready.... enjoy the food..."<<endl;
cout<<"this was the last state..."<<endl;
cout<<"i hope you liked the delicious aajwayni chicken..."<<endl;
mamma->isCookedStateDone = true;
}

//override
void goToNextState(){

}

//override
char* getTag(){
return tag;
}

};

#endif



#ifndef MAMMA_H_
#define MAMMA_H_

#include <iostream>

#include "timer.h"

#include "Callback.h"

using namespace std;
class ChickenState;
class UnleanedState;

class Mamma : public Callback{

private:

ChickenState* chicken;

ChickenState* currentState;




public:

Timer t;

bool timerRunning;

bool isUncleanedStateDone;

bool isCleanedStateDone;

bool isMarionatedStatedone;

bool isCookedStateDone;

Mamma();

virtual ~Mamma();

ChickenState* getChicken();

void changeChickenState(ChickenState* toChickenState);

void startPreparation(bool timerRunning);

void goToNextStateFromCallback();

inline ChickenState* getCurrentstate(){
return currentState;
}
};

#endif


/*
* Mamma.cpp
*
*  Created on: May 5, 2022
*      Author: som
*/

#include "Mamma.h"
#include "UncleanedState.h"
#include "timer.h"
#include <typeinfo>
#include <cstring>

Mamma::Mamma(){

isUncleanedStateDone = false;
isCleanedStateDone = false;
isMarionatedStatedone = false;
isCookedStateDone = false;
chicken = new UncleanedState(this);
currentState = chicken;
t.setCallback(this);
timerRunning = true;
}

Mamma::~Mamma(){
delete chicken;
delete currentState;
cout<<"Mamma - the cook is leaving the stage"<<endl;
}

ChickenState* Mamma::getChicken(){
return chicken;
}

void Mamma::changeChickenState(ChickenState* toChickenState){

chicken = toChickenState;
currentState = chicken;

}

void Mamma::startPreparation(bool timerRunning) {


if(!isUncleanedStateDone){
chicken->wash(1000, 3000);

}

//next state is CleanedState

if(isUncleanedStateDone){
chicken->marinate(1000, 3000);
}

//Next state is Marinated State
if(isCleanedStateDone){
chicken->cook(200, 5000);
}

//Next state is cookedState... this will be the last state
if(isMarionatedStatedone){
chicken->serve(200, 2000);
}

while (timerRunning);

}

void Mamma::goToNextStateFromCallback(){

timerRunning = false;

if(strstr (currentState->getTag(), "Uncleaned")){
ChickenState* nextState = new CleanedState(this);
changeChickenState(nextState);

//just stop the timer thread...
t.justStop();
timerRunning = true;
//recursion...
startPreparation(timerRunning);
}

if(strstr(currentState->getTag(), "Cleaned")){
ChickenState* nextState = new MarinatedState(this);
changeChickenState(nextState);
//just stop the timer...
t.justStop();
timerRunning = true;
//recursion...
startPreparation(timerRunning);
}

if(strstr(currentState->getTag(), "Marinated") ){
ChickenState* nextState = new CookedState(this);
changeChickenState(nextState);
t.justStop();
timerRunning = true;
//recursion...
startPreparation(timerRunning);
}

if(strstr(currentState->getTag(), "Cooked") ){
t.justStop();
timerRunning = false;
startPreparation(timerRunning);
}
}


#ifndef TIMER_H_
#define TIMER_H_

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

#include "Callback.h"

using namespace std;

class Timer {

private:

Callback* cb;

public:

Timer(){

}


void setCallback(Callback * cb){
this->cb = cb;
}


atomic_bool active = { true };


        void setTimeout(auto function, int delay){
        active = true;
            thread t([=]() {
                if(!active.load()) return;
                this_thread::sleep_for(chrono::milliseconds(delay));
                if(!active.load()) return;
                function();
            });
            t.join();
        }
        void setInterval(auto function, int interval){
        active = true;
            thread t([=]() {
                while(active.load()) {
                    this_thread::sleep_for(chrono::milliseconds(interval));
                    if(!active.load()) return;
                    function();
                }
            });
            t.detach();
        }
        void stop(){
        cb->goToNextStateFromCallback();

        }

        void justStop(){
        active = false;
        }

        void start(){
        active = true;
        }

};

#endif


#include <iostream>

#include <memory>

#include "Mamma.h"

using namespace std;

int main() {

unique_ptr<Mamma> mamma = make_unique<Mamma>();

mamma->startPreparation(true);

}


The Copiler and linker option for the above program is 








The video of the output if we run the above program will be as follows:
 

Here is the complete source code for the developers...

No comments: