Salah satu pola favorit saya adalah pola desain negara bagian. Menanggapi atau berperilaku berbeda untuk sekumpulan input yang sama.
Salah satu masalah dengan menggunakan pernyataan sakelar / kasus untuk mesin status adalah saat Anda membuat lebih banyak status, sakelar / kasing menjadi lebih sulit / berat untuk dibaca / dipelihara, mempromosikan kode spaghetti yang tidak terorganisir, dan semakin sulit untuk diubah tanpa merusak sesuatu. Saya menemukan penggunaan pola desain membantu saya mengatur data saya dengan lebih baik, yang merupakan inti dari abstraksi. Alih-alih mendesain kode negara Anda di sekitar negara bagian mana Anda berasal, alih-alih susun kode Anda sehingga ia mencatat keadaan saat Anda memasuki negara bagian baru. Dengan begitu, Anda secara efektif mendapatkan catatan tentang keadaan Anda sebelumnya. Saya menyukai jawaban @ JoshPetit, dan telah mengambil solusinya selangkah lebih maju, diambil langsung dari buku GoF:
stateCtxt.h:
#define STATE (void *)
typedef enum fsmSignal
{
eEnter =0,
eNormal,
eExit
}FsmSignalT;
typedef struct fsm
{
FsmSignalT signal;
// StateT is an enum that you can define any which way you want
StateT currentState;
}FsmT;
extern int STATECTXT_Init(void);
/* optionally allow client context to set the target state */
extern STATECTXT_Set(StateT stateID);
extern void STATECTXT_Handle(void *pvEvent);
stateCtxt.c:
#include "stateCtxt.h"
#include "statehandlers.h"
typedef STATE (*pfnStateT)(FsmSignalT signal, void *pvEvent);
static FsmT fsm;
static pfnStateT UsbState ;
int STATECTXT_Init(void)
{
UsbState = State1;
fsm.signal = eEnter;
// use an enum for better maintainability
fsm.currentState = '1';
(*UsbState)( &fsm, pvEvent);
return 0;
}
static void ChangeState( FsmT *pFsm, pfnStateT targetState )
{
// Check to see if the state has changed
if (targetState != NULL)
{
// Call current state's exit event
pFsm->signal = eExit;
STATE dummyState = (*UsbState)( pFsm, pvEvent);
// Update the State Machine structure
UsbState = targetState ;
// Call the new state's enter event
pFsm->signal = eEnter;
dummyState = (*UsbState)( pFsm, pvEvent);
}
}
void STATECTXT_Handle(void *pvEvent)
{
pfnStateT newState;
if (UsbState != NULL)
{
fsm.signal = eNormal;
newState = (*UsbState)( &fsm, pvEvent );
ChangeState( &fsm, newState );
}
}
void STATECTXT_Set(StateT stateID)
{
prevState = UsbState;
switch (stateID)
{
case '1':
ChangeState( State1 );
break;
case '2':
ChangeState( State2);
break;
case '3':
ChangeState( State3);
break;
}
}
statehandlers.h:
/* define state handlers */
extern STATE State1(void);
extern STATE State2(void);
extern STATE State3(void);
statehandlers.c:
#include "stateCtxt.h:"
/* Define behaviour to given set of inputs */
STATE State1(FsmT *fsm, void *pvEvent)
{
STATE nextState;
/* do some state specific behaviours
* here
*/
/* fsm->currentState currently contains the previous state
* just before it gets updated, so you can implement behaviours
* which depend on previous state here
*/
fsm->currentState = '1';
/* Now, specify the next state
* to transition to, or return null if you're still waiting for
* more stuff to process.
*/
switch (fsm->signal)
{
case eEnter:
nextState = State2;
break;
case eNormal:
nextState = null;
break;
case eExit:
nextState = State2;
break;
}
return nextState;
}
STATE State3(FsmT *fsm, void *pvEvent)
{
/* do some state specific behaviours
* here
*/
fsm->currentState = '2';
/* Now, specify the next state
* to transition to
*/
return State1;
}
STATE State2(FsmT *fsm, void *pvEvent)
{
/* do some state specific behaviours
* here
*/
fsm->currentState = '3';
/* Now, specify the next state
* to transition to
*/
return State3;
}
Untuk sebagian besar Mesin Negara, khususnya. Mesin keadaan terbatas, setiap keadaan akan tahu apa keadaan selanjutnya, dan kriteria untuk transisi ke keadaan berikutnya. Untuk desain status longgar, ini mungkin tidak terjadi, oleh karena itu opsi untuk mengekspos API untuk status transisi. Jika Anda menginginkan lebih banyak abstraksi, setiap penangan status dapat dipisahkan menjadi filenya sendiri, yang setara dengan penangan status konkret di buku GoF. Jika desain Anda sederhana dengan hanya beberapa status, maka stateCtxt.c dan statehandlers.c dapat digabungkan menjadi satu file untuk kesederhanaan.