這個是從同事那邊知道的好東西, 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轉換的程式複雜度
Asterisk-強悍的PBX平台 (2008-07-24 18:44)
1st JavaScript Editor Pro 3.8 memory patch for IntelliSense (2008-05-28 15:08)