c++ 狀態模式,消除switch-case 語句

/*author: hjjdebug
 * date: Thu Feb 27 21:59:29 CST 2020
 */
/* c++ 狀態模式,消除switch-case 語句
 * 前言: 有一個系統,包含了5個project, 但是,每次運行只會運行一個project,
 * 運行哪個靠一個projnum來決定, 所以代碼裏到處都是switch(projnum)的語句,
 * 難道就沒有更好的辦法來處理這種情況了嗎? 我研究了一下c++的狀態模式,可以
 * 很好解決這個問題, 下面給出一個最典型的例子和解決方案.
 *
 * 網上講模式的很多,但多不是c++的,也缺乏典型例子,所以這裏補充一下實例. uml圖就不劃了.
 */

 

#include <iostream>
#include <stdio.h>

using namespace std;

int main()
{
	int num=5;
	switch(num)
	{
		case 1:
			cout<< "I was in state1"<<endl;
			break;
		case 3:
			cout<< "I was in state3"<<endl;
			break;
		case 5:
			cout<< "I was in state5"<<endl;
			break;
		default:
			cout << "can't find  any state"<<endl;

	}
	return 0;
}

功能: 根據傳入的一個變量,打印出一條語句.
這個代碼如此簡單,還有優化的餘地嗎?
真要是這麼小代碼,其實不用優化,正因爲它小,所以才凸顯方法的重要.
當case 內容比較複雜時,就很有優化必要了,
而且,switch-case 不符合開放-封閉原則,當有新的case 要添加時,必然要修改其中的代碼. 使得代碼封閉不了!
我們就用這個小麻雀爲例,用狀態模式進行優化.

1. 將case1,case3,case5下的具體內容用3個類來包裝,分別叫state1,state3,state5, 它們共同繼承一個基類state.
2. 基類有一個虛方法叫handleIt, 在state1,state3,state5中handleIt中分別實現對應的case中的處理語句.就是cout語句
3. num 是context類的一個屬性, case 1是state1中的一個比較語句
    switch 當中的比較語句, 是傳來的一個數num, 跟固有的一個數的比較,例如state1跟1比,state3跟3比,state5跟5比.
    我們把num可以包在一個對象中,叫context,它是context的一個屬性.
4. 狀態類,滿足要求輸出處理結果,不滿足要求轉讓下一個狀態處理.
5. context 類
    context 應包含一個狀態指針,可以指向具體的狀態對象.
    context 應該包含一個setState(IState *)方法,以便保存狀態對象
    context 也要有一個handleIt方法,它會轉而調用state的handleIt方法,從而調用了具體state類的handleIt方法.
    context 是與用戶打交到的類,應該有setnum方法. 把num 存爲自己的屬性

數據處理過程:
創建context對象,設置對象state狀態和num, 然後調用context的handleIt方法,轉而調用具體state類的handleIt方法.
具體state類的handleIt方法,如果num 與自己的值相等,則輸出打印語句,否則,創建下一個狀態對象,設置給context,
調用context 的handleIt方法,

這樣就又回到起始點,context用狀態對象進行比較判斷和處理.直到有一個對象接受處理爲止或者全部不接受.
完整的代碼如下所示: 用一個文件就可以編譯運行了.

如果是大項目代碼,則把Context,IState, IState1,IState3,IState5 分別用不同的文件保存.
當再添加狀態時,例如添加IState6, 則Context,IState, IState1,IState3 代碼都不用動,也不用再編譯,完成了代碼封閉
IState5 要稍微修改一點(不可能做到完全封閉),然後再添加IState6代碼就可以了,完成代碼開放原則

#include <iostream>
#include <stdio.h>

using namespace std;


class Context;
class IState1;
class IState3;
class IState5;

class IState
{
public:
	IState(){}
	~IState(){}
	virtual void handleIt(Context *ct)=0;
};
class Context
{
public:
	Context(){}
	~Context(){}
	void setState(IState *st)
	{
		istate = st;
	}
	void handleIt(){
		istate->handleIt(this);
	}
	void setnum(int n)
	{
		num = n;
	}
	int num;
private:
	IState *istate;
};
class IState5 : public IState
{
public:
	IState5(){}
	~IState5(){}
	virtual void handleIt(Context *ct)
	{
		if(ct->num == 5)
		{
			cout<< "I was in state5"<<endl;
		}
		else
		{
			cout << "can't find  any state"<<endl;
		}
	}
};
class IState3 : public IState
{
public:
	IState3(){}
	~IState3(){}
	virtual void handleIt(Context *ct)
	{
		if(ct->num == 3)
		{
			cout<< "I was in state3"<<endl;
		}
		else
		{
			ct->setState(new IState5());
			ct->handleIt();
		}
	}
};
class IState1 : public IState
{
public:
	IState1(){}
	~IState1(){}
	virtual void handleIt(Context *ct)
	{
		if(ct->num == 1)
		{
			cout<< "I was in state1"<<endl;
		}
		else
		{
			ct->setState(new IState3());
			ct->handleIt();
		}
	}
};


//代碼調用簡單了嗎? 那是有其它大量代碼支持才簡化的
//它滿足開放,封閉原則
int main(void)
{
	//創建對象並設置狀態
	Context *ct = new Context();
	ct->setState(new IState1());
	//傳入一個比較數值
	ct->setnum(5);
	//context 調用handleIt,傳到具體的state調用handleIt
	//如果匹配不到,會繼續調用後續的狀態來handleIt,直到匹配
	ct->handleIt();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章