這個是從同事那邊知道的好東西, Nunni FSM Generator,它能自動根據你輸入的設定檔產生狀態機的程式碼,詳細的資料請查閱軟體的使用說明,但我要講的重點在於它”自動產生的程式碼部份”,因為自動產生的code封裝得十分漂亮,所以我想對code做點介紹
以前寫狀態機的東西,不脫離if,else,case,switch…等這幾種語法搭配,可是寫出來的code”不大容易讀懂”,比如說我今天有個狀態機如下圖,假設有一張桌子,上面只能擺一個瓶子,我如果拿走了這個瓶子,那我就不能從桌子上再拿走瓶子了,如果桌子上已經擺了一個瓶子,那桌子上就再也不能多加瓶子上去
如果用if-else語法寫寫看,可能會像下面這樣
#include <stdio.h>
int bottles=1;
void GetBottleAPI()
{
if (bottles==1)
{
printf("You got bottle\n");
bottles--;
}
else printf("No bootle to get\n");
}
void PutBottleAPI()
{
if (bottles==0)
{
printf("You put a bottle\n");
bottles++;
}
else printf("Too much bootles\n");
}
int main()
{
GetBottleAPI();
GetBottleAPI();
PutBottleAPI();
PutBottleAPI();
GetBottleAPI();
}
用int bottles當global變數判斷get or put的動作是否合法,這種寫法如果用在小型的狀態機還OK,如果狀態機又多又大又複雜,可以想見程式碼會為這些狀態多寫好幾個判斷變數
試試看下面的程式碼,我只要定義好每個狀態與外來動作的觸發機制,就可以寫出蠻漂亮的code
#include <stdio.h>
struct TableState ;
struct TableFSM
{
int (*GetBottleStatus)( struct TableFSM *fsm, void * o );
int (*PutBottleStatus)( struct TableFSM *fsm, void * o );
void (*changeState)( struct TableFSM *fsm, struct TableState *nextState );
struct TableState *m_state;
};
struct TableState
{
int (*GetBottleStatus)( struct TableFSM *fsm, void * o );
int (*PutBottleStatus)( struct TableFSM *fsm, void * o );
};
static struct TableState TableFull;
static struct TableState TableEmpty;
void GetBottle()
{
printf("You get a bottle\n");
}
void GiveBottle()
{
printf("You put bottle on the table\n");
}
void NoBottle()
{
printf("There is no bottle on the table\n");
}
void BottleFull()
{
printf("There is too much bottle on the table\n");
}
int FSMGetBottleStat( struct TableFSM *fsm, void * o )
{
return fsm->m_state->GetBottleStatus( fsm, o );
}
int FSMPutBottleStat( struct TableFSM *fsm, void * o )
{
return fsm->m_state->PutBottleStatus( fsm, o );
}
void FSMChangeState( struct TableFSM *fsm, struct BottleState *newState )
{
fsm->m_state = newState;
}
int GetBottleAPI( struct TableFSM *fsm, void * o )
{
GetBottle();
fsm->changeState( fsm, &TableEmpty);
}
int GiveBottleAPI(struct TableFSM *fsm, void * o )
{
GiveBottle();
fsm->changeState( fsm, &TableFull );
}
int TooFewBottleAPI(struct TableFSM *fsm, void * o )
{
NoBottle();
fsm->changeState( fsm, &TableEmpty );
}
int TooMuchBottleAPI(struct TableFSM *fsm, void * o )
{
BottleFull();
fsm->changeState( fsm, &TableFull );
}
int main()
{
static struct TableFSM bf;
TableFull.GetBottleStatus = GetBottleAPI;
TableFull.PutBottleStatus = TooMuchBottleAPI;
TableEmpty.GetBottleStatus = TooFewBottleAPI;
TableEmpty.PutBottleStatus = GiveBottleAPI;
bf.GetBottleStatus=FSMGetBottleStat;
bf.PutBottleStatus=FSMPutBottleStat;
bf.changeState=FSMChangeState;
bf.m_state=&TableFull;
FSMGetBottleStat(&bf,0);
FSMGetBottleStat(&bf,0);
FSMPutBottleStat(&bf,0);
FSMPutBottleStat(&bf,0);
}
上面的程式碼相當簡潔,當外部動作觸發內部狀態改變,會把FSM指定的函式指定到轉入後的state callback,所以programmer可以完全專注在GetBottleAPI之類的函式實作,而不用考慮state轉換的程式複雜度
留言