引言:在軟體開發中,狀態機(State Machine)是一種重要的概念,用於描述物件或系統在不同狀態之間的轉換和行為。理解狀態機對於開發人員來說是至關重要的,特別是在處理復雜的業務邏輯或狀態管理時。本文將深入探討什麽是狀態機,以及它在軟體開發中的套用。
題目
小黑子!面試官問我有用過狀態機嗎?
推薦解析
狀態機的組成
狀態機是一種抽象的數學模型,描述了物件或系統在特定時間點可能處於的各種狀態以及狀態之間的轉換規則。它由一組狀態、事件、轉移和動作組成,用於模擬物件在不同條件下的行為和狀態變化。
狀態機包括以下基本組成部份:
狀態(State):表示物件或系統當前的狀態,例如開、關、就緒等。
事件(Event):觸發狀態轉換的動作或條件,例如按鈕點選、訊息到達等。
轉移(Transition):定義了從一個狀態到另一個狀態的轉換規則,通常與特定事件相關聯。
動作(Action):在狀態轉換過程中執行的操作或行為,例如更新狀態、記錄日誌等。
狀態機,也就是 State Machine ,不是指一台實際機器,而是指一個數學模型。說白了,一般就是指一張狀態轉換圖。例如,根據自動門的執行規則,我們可以抽象出下面這麽一個圖。
簡單實作
在電腦中,狀態機通常用程式語言來實作。在 C、C++、Java、Python 等程式語言中,可以透過使用 switch-case 語句、if-else 語句、狀態轉移表等來實作狀態機。在下面還有更加優雅的方式,使用 Spring 狀態機 來實作。
if-else 實作狀態機
在上面的範例中,我們使用
if-else
結構根據當前活動來控制音樂的播放狀態,並執行相應的行為。程式碼如下:
public classBasketballMusicStateMachineUsingIfElse{
privateboolean isPlayingMusic;
publicBasketballMusicStateMachineUsingIfElse(){
this.isPlayingMusic = false; // 初始狀態為音樂未播放
}
publicvoidplayMusic(){
if (!isPlayingMusic) {
System.out.println("Music starts playing...");
isPlayingMusic = true;
}
}
publicvoidstopMusic(){
if (isPlayingMusic) {
System.out.println("Music stops playing...");
isPlayingMusic = false;
}
}
publicvoidperformActivity(String activity){
if ("basketball".equals(activity)) {
System.out.println("Music~");
playMusic(); // 打籃球時播放音樂
} elseif ("sing_rap".equals(activity)) {
System.out.println("哎喲你幹嘛!");
stopMusic(); // 唱跳Rap時停止音樂
} else {
System.out.println("Invalid activity!");
}
}
publicstaticvoidmain(String[] args){
BasketballMusicStateMachineUsingIfElse stateMachine = new BasketballMusicStateMachineUsingIfElse();
// 測試狀態機
stateMachine.performActivity("basketball"); // 打籃球,音樂開始播放
stateMachine.performActivity("sing_rap"); // 唱跳Rap,音樂停止播放
stateMachine.performActivity("basketball"); // 再次打籃球,音樂重新開始播放
}
}
switch-case 實作狀態機
在這個範例中,我們使用
switch-case
結構根據不同的活動來控制音樂的播放狀態,並執行相應的行為。程式碼如下:
public classBasketballMusicStateMachineUsingSwitchCase{
privateboolean isPlayingMusic;
publicBasketballMusicStateMachineUsingSwitchCase(){
this.isPlayingMusic = false; // 初始狀態為音樂未播放
}
publicvoidplayMusic(){
if (!isPlayingMusic) {
System.out.println("Music starts playing...");
isPlayingMusic = true;
}
}
publicvoidstopMusic(){
if (isPlayingMusic) {
System.out.println("Music stops playing...");
isPlayingMusic = false;
}
}
publicvoidperformActivity(String activity){
switch (activity) {
case"basketball":
System.out.println("Music ~");
playMusic(); // 打籃球時播放音樂
break;
case"sing_rap":
System.out.println("哎喲 你幹嘛 ~");
stopMusic(); // 唱跳Rap時停止音樂
break;
default:
System.out.println("Invalid activity!");
}
}
publicstaticvoidmain(String[] args){
BasketballMusicStateMachineUsingSwitchCase stateMachine = new BasketballMusicStateMachineUsingSwitchCase();
// 測試狀態機
stateMachine.performActivity("basketball"); // 打籃球,音樂開始播放
stateMachine.performActivity("sing_rap"); // 唱跳Rap,音樂停止播放
stateMachine.performActivity("basketball"); // 再次打籃球,音樂重新開始播放
}
}
是不是感覺狀態機其實經常在我們的日常使用中捏~,接下來帶大家使用更優雅的狀態機 Spring 狀態機。
使用 Spring 狀態機
1)引入依賴
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
2)定義狀態和事件的列舉
程式碼如下:
publicenum States {
IDLE, // 空閑狀態
PLAYING_BB, // 打籃球狀態
SINGING // 唱跳Rap狀態
}
publicenum Event {
START_BB_MUSIC, // 開始播放籃球音樂事件
STOP_BB_MUSIC // 停止籃球音樂事件
}
3)配置狀態機
程式碼如下:
@Configuration
@EnableStateMachine
public classBasketballMusicStateMachineConfigextendsEnumStateMachineConfigurerAdapter<States, Event> {
@Autowired
private BasketballMusicStateMachineEventListener eventListener;
@Override
publicvoidconfigure(StateMachineConfigurationConfigurer<States, Event> config)throws Exception {
config
.withConfiguration()
.autoStartup(true)
.listener(eventListener); // 設定狀態機事件監聽器
}
@Override
publicvoidconfigure(StateMachineStateConfigurer<States, Event> states)throws Exception {
states
.withStates()
.initial(States.IDLE)
.states(EnumSet.allOf(States. class));
}
@Override
publicvoidconfigure(StateMachineTransitionConfigurer<States, Event> transitions)throws Exception {
transitions
.withExternal()
.source(States.IDLE).target(States.PLAYING_BB).event(Event.START_BB_MUSIC)
.and()
.withExternal()
.source(States.PLAYING_BB).target(States.SINGING).event(Event.STOP_BB_MUSIC)
.and()
.withExternal()
.source(States.SINGING).target(States.PLAYING_BB).event(Event.START_BB_MUSIC);
}
}
4)定義狀態機事件監聽器
程式碼如下:
@Component
public classBasketballMusicStateMachineEventListenerextendsStateMachineListenerAdapter<States, Event> {
@Override
publicvoidstateChanged(State<States, Event> from, State<States, Event> to){
if (from.getId() == States.IDLE && to.getId() == States.PLAYING_BB) {
System.out.println("開始打籃球,music 起");
} elseif (from.getId() == States.PLAYING_BB && to.getId() == States.SINGING) {
System.out.println("唱跳,你幹嘛");
} elseif (from.getId() == States.SINGING && to.getId() == States.PLAYING_BB) {
System.out.println("繼續打籃球,music 繼續");
}
}
}
5)編寫單元測試
@SpringBootTest
classChatApplicationTests{
@Resource
private StateMachine<States, Event> stateMachine;
@Test
voidcontextLoads(){
//開始打球,music 起
stateMachine.sendEvent(Event.START_BB_MUSIC);
//開始唱跳,你幹嘛
stateMachine.sendEvent(Event.STOP_BB_MUSIC);
//繼續打球,music 繼續
stateMachine.sendEvent(Event.START_BB_MUSIC);
}
}
效果如下:
在上面的範例中,我們定義了一個狀態機,用於控制在打籃球時音樂的播放和唱跳 Rap 的行為。透過觸發事件來執行狀態轉移,並透過事件監聽器監聽狀態變化並執行相應的操作。
推薦文章和書籍
文章:https://zhuanlan.zhihu.com/p/86293659
書籍:【 Java 核心技術卷 I 】
歡迎交流
當談到狀態機時,我們可以探討以下幾個問題:
1)什麽是狀態機?狀態機在軟體開發中有什麽套用?
2)狀態機的基本組成和工作原理是什麽?
3)狀態機和有限狀態機(FSM)有何區別?
這些問題將幫助我們深入了解狀態機的概念、套用和原理,從而更好地套用狀態機解決實際問題。
點燃求職熱情!每周持續更新,海量面試題和面經等你挑戰!趕緊關註面試鴨公眾號,輕松備戰春招和暑期實習!
往期推薦