站在华为上汽面试官的角度去想问题-ATM状态机又撒了壹万亿

引言

记得年轻的时候去面试一家大厂的嵌入式软件工程师的职位,面试官让很多候选人三人一组讨论去设计一款ATM机,当时我们那组讨论的热火朝天,什么把Linux操作系统内核移植过来,加入互锁机制确保取钱的安全性,什么多线程加快速度等等天马行空的方案。最终默默地在等待Offer,就一直默默而无下文了。现在站在大厂的角度想一想,可能是通过一些试题想考察你对一个项目的软件思维能力。啥叫软件思维能力。就是从软件的角度去解读需求。别囫囵吞枣,大而空地泛泛之谈。你能想得越细,设计的代码覆盖的case越多,设计的代码简单且RAM,ROM越节省,执行效率越高,这才是去做嵌入式软件工程师的正确解法。

我们从状态机的角度去分析一下解题步骤:

全面详尽的弄清楚客户的需求,分解成相应的功能或多个子功能需求

弄清楚这些功能中的状态,并确定状态的转移条件(及状态的进入和退出条件或事件),设计出系统的状态骨架

测试每个转移的有效性

针对每个状态,结合功能需求细化状态中需要做哪些事情

集成进行系统的集成测试

ATM状态机

当您设计ATM 的功能时,设想一下你是小明。

小明走到ATM机面前,一系列的事件(事件是指在某个时刻发生的事情

)发生了,插入卡片(Card_Insert_Event),输入密码(Pin_Enter_Even),选择办理事项(Option_Selection_Event:取钱/存钱/查询余额),输入数额(Amount_Enter_Event)并按确认,ATM吐出所需钱财(Amount_Dispatch_Event)。如下图为从ATM取钱的一个状态机模型,

描述了ATM状态及转移条件。

状态作为系统的一种特定阶段的状态,则会持续一段时间,直到特定的触发事件导致状态的转移的特定点为止。例如夜深人静的时候ATM大概率处于空闲状态。过节的时候(是蛮多和女生相关的且需要花钱的节日的),你走到ATM插入卡片后的那个时间点,ATM从空闲状态切换到了检测到卡片插入状态。

对事件发生所执行的事件处理是当前状态和输入事件的一个函数。此处的名字叫做EventHandle, 还可叫做Action(有三类:entry,during,exit)。其可存在转移的分支上,或者是位于状态的进入或退出动作或during标签中,如下图所示。

entry: 当进入一个状态的时候执行该标签下的action,该action在状态中其它任何action之前执行。

during: 当状态处于激活时执行during 标签下的action , during活动在进入活动之后执行,并且—直运行到它本身完成为止(同步执行的动作)。

exit: 当离开—个状态的时候触发执行该标签下的action,该活动在该状态结束之前并且所有其它action都完成后触发执行。

一般状态之间的转换不仅依赖于Event的发生,还需要[Condition Expression]这个门控条件成立,才能发生转移。

如下图中的转移的方式:转移条件分别为Condition和Event。

状态机实现方式

如果你用C语言建模的话,一般推荐下面两种方式

实现方式1:switch-case结构。

实现方式简单,Case变多的时候不易维护,并且圈复杂度较高。

#include<stdio.h>

//Different state of ATM machine

typedefenum

{

Idle_State,

Card_Inserted_State,

Pin_Eentered_State,

Option_Selected_State,

Amount_Entered_State,

} eSystemState;

//Different type events

typedefenum

{

Card_Insert_Event,

Pin_Enter_Event,

Option_Selection_Event,

Amount_Enter_Event,

Amount_Dispatch_Event

} eSystemEvent;

//Prototype of eventhandlers

eSystemStateAmountDispatchHandler(void)

{

returnIdle_State;

}

eSystemStateEnterAmountHandler(void)

{

returnAmount_Entered_State;

}

eSystemStateOptionSelectionHandler(void)

{

returnOption_Selected_State;

}

eSystemStateEnterPinHandler(void)

{

returnPin_Eentered_State;

}

eSystemStateInsertCardHandler(void)

{

returnCard_Inserted_State;

}

intmain(intargc,char*argv[])

{

eSystemState eNextState = Idle_State;

eSystemEvent eNewEvent;

while(1)

{

//Read system Events

eSystemEvent eNewEvent = ReadEvent();

switch(eNextState)

{

caseIdle_State:

{

if(Card_Insert_Event == eNewEvent)

{

eNextState = InsertCardHandler();

}

}

break;

caseCard_Inserted_State:

{

if(Pin_Enter_Event == eNewEvent)

{

eNextState = EnterPinHandler();

}

}

break;

casePin_Eentered_State:

{

if(Option_Selection_Event == eNewEvent)

{

eNextState = OptionSelectionHandler();

}

}

break;

caseOption_Selected_State:

{

if(Amount_Enter_Event == eNewEvent)

{

eNextState = EnterAmountHandler();

}

}

break;

caseAmount_Entered_State:

{

if(Amount_Dispatch_Event == eNewEvent)

{

eNextState = AmountDispatchHandler();

}

}

break;

default:

break;

}

}

return0;

}

实现方式2:查表方式

易于维护,可以方便你增加新的状态或事件。减少代码的长度,通过函数指针和事件及状态进行绑定,非要说个缺点就是使用了指针,在汽车Misra C标准中不太推荐使用指针。

#include<stdio.h>

//Different state of ATM machine

typedefenum

{

Idle_State,

Card_Inserted_State,

Pin_Eentered_State,

Option_Selected_State,

Amount_Entered_State,

last_State

} eSystemState;

//Different type events

typedefenum

{

Card_Insert_Event,

Pin_Enter_Event,

Option_Selection_Event,

Amount_Enter_Event,

Amount_Dispatch_Event,

last_Event

} eSystemEvent;

//typedef of function pointer

typedefeSystemState(*pfEventHandler)(void);

//structure of state and event with event handler

typedefstruct

{

eSystemState eStateMachine;

eSystemEvent eStateMachineEvent;

pfEventHandler pfStateMachineEvnentHandler;

} sStateMachine;

//function call to dispatch the amount and return the ideal state

eSystemStateAmountDispatchHandler(void)

{

returnIdle_State;

}

//function call to Enter amount and return amount entered state

eSystemStateEnterAmountHandler(void)

{

returnAmount_Entered_State;

}

//function call to option select and return the option selected state

eSystemStateOptionSelectionHandler(void)

{

returnOption_Selected_State;

}

//function call to enter the pin and return pin entered state

eSystemStateEnterPinHandler(void)

{

returnPin_Eentered_State;

}

//function call to processing track data and return card inserted state

eSystemStateInsertCardHandler(void)

{

returnCard_Inserted_State;

}

//Initialize array of structure with states and event with proper handler

sStateMachine asStateMachine [] =

{

{Idle_State,Card_Insert_Event,InsertCardHandler},

{Card_Inserted_State,Pin_Enter_Event,EnterPinHandler},

{Pin_Eentered_State,Option_Selection_Event,OptionSelectionHandler},

{Option_Selected_State,Amount_Enter_Event,EnterAmountHandler},

{Amount_Entered_State,Amount_Dispatch_Event,AmountDispatchHandler}

};

//main function

intmain(intargc,char*argv[])

{

eSystemState eNextState = Idle_State;

while(1)

{

//Api read the event

eSystemEvent eNewEvent = read_event();

if((eNextState < last_State) && (eNewEvent < last_Event)&& (asStateMachine[eNextState].eStateMachineEvent == eNewEvent) && (asStateMachine[eNextState].pfStateMachineEvnentHandler !=NULL))

{

// function call as per the state and event and return the next state of the finite state machine

eNextState = (*asStateMachine[eNextState].pfStateMachineEvnentHandler)();

}

else

{

//Invalid

}

}

return0;

}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容