/*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;
}