本章以旋轉閘門(Turnstile)的有限狀態機(FSM)為範例,深入探討 STATE 模式的三種實作策略,並比較 STATE 與 STRATEGY 模式的異同。
有限狀態機:旋轉閘門#

Figure 36.1: Turnstile FSM that covers abnormal events
- 旋轉閘門有兩個狀態:Locked(上鎖)與 Unlocked(解鎖)
- 兩個事件:Coin(投幣)與 Pass(通過)
- 轉換規則:
- Locked + Coin → Unlocked(解鎖門閘)
- Unlocked + Pass → Locked(鎖上門閘)
- Locked + Pass → 保持 Locked(觸發警報)
- Unlocked + Coin → 保持 Unlocked(退幣)
stateDiagram-v2
[*] --> Locked
Locked --> Unlocked : Coin / Unlock
Unlocked --> Locked : Pass / Lock
Locked --> Locked : Pass / Alarm
Unlocked --> Unlocked : Coin / Thankyou實作策略一:巢狀 Switch/Case#
- 最簡單直接的實作方式:用列舉表示狀態與事件,以巢狀
switch處理轉換
public class Turnstile
{
internal enum State { Locked, Unlocked }
internal State state = State.Locked;
public void HandleEvent(Event e)
{
switch (state)
{
case State.Locked:
switch (e)
{
case Event.Coin:
state = State.Unlocked;
Unlock();
break;
case Event.Pass:
Alarm();
break;
}
break;
case State.Unlocked:
switch (e)
{
case Event.Coin:
Thankyou();
break;
case Event.Pass:
state = State.Locked;
Lock();
break;
}
break;
}
}
}- 優點:簡單、容易理解、效能好
- 缺點:狀態與事件增多時,switch 會變得龐大且難以維護
實作策略二:轉換表(Transition Table)#
- 使用資料驅動的方式:將所有轉換定義在一個表格中,用查表取代 switch
private void AddTransition(
State currentState, Event trigger,
State newState, Action action)
{
transitions.Add(new Transition(currentState, trigger, newState, action));
}
// 建立轉換表
AddTransition(State.Locked, Event.Coin, State.Unlocked, Unlock);
AddTransition(State.Locked, Event.Pass, State.Locked, Alarm);
AddTransition(State.Unlocked, Event.Coin, State.Unlocked, Thankyou);
AddTransition(State.Unlocked, Event.Pass, State.Locked, Lock);- 優點:轉換邏輯集中在表格中,易於修改與閱讀
- 缺點:查表有輕微效能損失,且不易處理複雜的轉換條件
實作策略三:STATE 模式#

Figure 36.3: STATE versus STRATEGY
- 使用物件導向的方式:每個狀態是一個類別,實作相同的介面
public interface TurnstileState
{
void Coin(Turnstile t);
void Pass(Turnstile t);
}
public class LockedTurnstileState : TurnstileState
{
public void Coin(Turnstile t)
{
t.SetUnlocked();
t.Unlock();
}
public void Pass(Turnstile t)
{
t.Alarm();
}
}
public class UnlockedTurnstileState : TurnstileState
{
public void Coin(Turnstile t)
{
t.Thankyou();
}
public void Pass(Turnstile t)
{
t.SetLocked();
t.Lock();
}
}Turnstile類別持有一個TurnstileState參考,事件發生時委派給當前狀態處理狀態的切換由狀態物件本身決定(呼叫
t.SetLocked()或t.SetUnlocked())優點:符合 OCP——新增狀態只需新增類別,不需修改既有程式碼;每個狀態的邏輯高度內聚
缺點:類別數量隨狀態數量增長;簡單的 FSM 用 STATE 模式可能過度設計
技巧: 狀態物件通常是無狀態的(Stateless),因此可以使用 Singleton 或靜態實例來避免重複建立。
Turnstile類別可以持有所有狀態的靜態實例。
STATE vs. STRATEGY#
- 結構上完全相同——兩者都是 Context 持有一個策略/狀態介面的參考
- 語義上不同:
- STRATEGY:Context 在建立時選擇一個策略,之後通常不再改變
- STATE:Context 的狀態會隨事件動態切換——這是關鍵區別
- STATE 模式中,狀態的轉換邏輯由狀態物件自己或 Context 控制
SMC 狀態機編譯器#
- 當 FSM 變得複雜時,手動編寫狀態類別會很繁瑣
- 作者介紹了 SMC(State Machine Compiler):一個將文字描述的 FSM 自動產生程式碼的工具
- SMC 的輸入是簡潔的狀態轉換定義,輸出是完整的 STATE 模式程式碼
進階應用#
GUI 互動#

Figure 36.5: Rectangle interaction state machine
- 繪圖程式中的滑鼠互動可以用 FSM 建模:
- 初始狀態 → 滑鼠按下 → 拖曳狀態 → 滑鼠放開 → 完成
- 拖曳時根據滑鼠移動更新矩形預覽
- STATE 模式讓這類複雜的互動邏輯清晰可維護
分散式處理#

Figure 36.6: Sending large block, using many packets
- 網路通訊中的大區塊傳輸:將大資料切成小封包,使用 FSM 管理傳送與確認流程
- 三層 FSM 架構:
- 頂層:管理連線的建立與中斷
- 中層:管理區塊的傳送與確認
- 底層:管理封包的傳送與重試
重點: 選擇 FSM 實作策略時,應根據複雜度而定:簡單的 FSM 用 switch/case 即可;中等複雜度用轉換表;複雜或需要頻繁修改的 FSM 用 STATE 模式或 SMC 工具。
本章小結#
STATE 模式將有限狀態機的每個狀態封裝為獨立的類別,讓狀態轉換邏輯清晰且易於擴展。三種實作策略(switch/case、轉換表、STATE 模式)各有優劣,應根據 FSM 的規模與變化頻率來選擇。STATE 與 STRATEGY 在結構上相同但語義不同——STATE 的核心特徵是狀態的動態切換。