Một trong những mẫu yêu thích của tôi là mẫu thiết kế trạng thái. Phản hồi hoặc hành xử khác nhau với cùng một nhóm đầu vào nhất định.
Một trong những vấn đề khi sử dụng câu lệnh switch / case cho các máy trạng thái là khi bạn tạo nhiều trạng thái hơn, switch / case trở nên khó đọc / khó sử dụng hơn để đọc / bảo trì, thúc đẩy mã spaghetti không có tổ chức và ngày càng khó thay đổi mà không làm hỏng thứ gì đó. Tôi thấy việc sử dụng các mẫu thiết kế giúp tôi tổ chức dữ liệu của mình tốt hơn, đó là toàn bộ điểm trừu tượng. Thay vì thiết kế mã trạng thái của bạn xung quanh trạng thái bạn đến, thay vào đó hãy cấu trúc mã của bạn để mã ghi lại trạng thái khi bạn nhập trạng thái mới. Bằng cách đó, bạn có được một bản ghi về trạng thái trước đó của mình một cách hiệu quả. Tôi thích câu trả lời của @ JoshPetit và đã thực hiện giải pháp của anh ấy thêm một bước nữa, lấy ngay từ sách của 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;
}
Đối với hầu hết các Máy Nhà nước, đặc biệt. Các máy trạng thái hữu hạn, mỗi trạng thái sẽ biết trạng thái tiếp theo của nó là gì và các tiêu chí để chuyển sang trạng thái tiếp theo của nó. Đối với các thiết kế trạng thái lỏng lẻo, điều này có thể không đúng, do đó tùy chọn để hiển thị API cho các trạng thái chuyển đổi. Nếu bạn muốn trừu tượng hơn, mỗi trình xử lý trạng thái có thể được tách ra thành tệp riêng của nó, tương đương với các trình xử lý trạng thái cụ thể trong sách GoF. Nếu thiết kế của bạn đơn giản với chỉ một vài trạng thái, thì cả stateCtxt.c và statehandlers.c đều có thể được kết hợp thành một tệp duy nhất để đơn giản hóa.