首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

設計模式實戰:State 模式重構自動販賣機,告別龐大 Switch-Case

     編輯:關於C語言

前言:有限狀態機的痛點

每位開發者職涯中都會實作有限狀態機(FSM):訂單流程、遊戲 AI、工作流引擎、通訊協定解析。最直覺的寫法是「查表法」或巨大的 switch-case,但隨著狀態與轉移條件增加,程式碼會變得:

  • 難以擴充:新增狀態需修改核心邏輯,違反開放封閉原則
  • 難以測試:所有轉移邏輯纏繞在單一方法
  • 難以維護:狀態行為分散在條件判斷中,缺乏內聚性

State 模式將每個狀態封裝為獨立類別,將轉移邏輯分散到各狀態內部,實現「狀態即物件」,完美解決上述痛點。

案例:自動販賣機

需求:投入 5、10、25 分硬幣,累計 ≥ 25 分出貨並找零(本例簡化不找零),歸零重置。

UML 核心結構


+----------------+       +------------------+
|  VendingMachine|------>|      State       |
|  (Context)     |       |  (Abstract)      |
| - state: State |       | + AddNickel()    |
| + AddNickel()  |       | + AddDime()      |
| + AddDime()    |       | + AddQuarter()   |
| + AddQuarter() |       | + ChangeState()  |
| + Vend()       |       +------------------+
+----------------+                ▲
          ▲                       │
          │           +-----------+-----------+
          │           │           │           │
      +---+---+   +---+---+   +---+---+   +---+---+
      | Start |   | Five  |   | Ten   |   |TwentyFive|
      +-------+   +-------+   +-------+   +---------+

C# 完整實作

1. 抽象狀態基類


public abstract class State
{
    protected VendingMachine Machine { get; private set; }

    public void SetMachine(VendingMachine machine) => Machine = machine;

    public virtual void AddNickel()  { ThrowInvalid(); }
    public virtual void AddDime()    { ThrowInvalid(); }
    public virtual void AddQuarter() { ThrowInvalid(); }

    protected void TransitionTo(State nextState)
    {
        Machine.TransitionTo(nextState);
    }

    private void ThrowInvalid() => throw new InvalidOperationException("當前狀態不允許此操作");
}

2. 具體狀態類別(單例模式)


public class StartState : State
{
    private static readonly Lazy _instance = new Lazy(() => new StartState());
    public static StartState Instance => _instance.Value;

    private StartState() { }

    public override void AddNickel()  => TransitionTo(FiveState.Instance);
    public override void AddDime()    => TransitionTo(TenState.Instance);
    public override void AddQuarter() { Machine.Vend(); TransitionTo(StartState.Instance); }

    public override string ToString() => "Credit: 0¢";
}

public class FiveState : State
{
    private static readonly Lazy _instance = new Lazy(() => new FiveState());
    public static FiveState Instance => _instance.Value;
    private FiveState() { }

    public override void AddNickel()  => TransitionTo(TenState.Instance);
    public override void AddDime()    => TransitionTo(FifteenState.Instance);
    public override void AddQuarter() { Machine.Vend(); TransitionTo(StartState.Instance); }

    public override string ToString() => "Credit: 5¢";
}

// TenState, FifteenState, TwentyFiveState 同理...

3. Context:自動販賣機


public class VendingMachine
{
    private State _currentState;

    public VendingMachine()
    {
        _currentState = StartState.Instance;
        _currentState.SetMachine(this);
        Console.WriteLine("自動販賣機啟動,商品 25¢");
        Console.WriteLine(_currentState);
    }

    public void TransitionTo(State newState)
    {
        _currentState = newState;
        _currentState.SetMachine(this);
        Console.WriteLine(_currentState);
    }

    public void Vend() => Console.WriteLine(">>> 出貨!謝謝惠顧 <<<");

    // 對外暴露動作,委派給目前狀態
    public void AddNickel()  => _currentState.AddNickel();
    public void AddDime()    => _currentState.AddDime();
    public void AddQuarter() => _currentState.AddQuarter();
}

4. 客戶端測試


var vm = new VendingMachine();
vm.AddNickel();   // 5¢
vm.AddNickel();   // 10¢
vm.AddDime();     // 20¢
vm.AddNickel();   // 25¢ -> 出貨,歸零
vm.AddQuarter();  // 直接出貨

State 模式 vs 查表法 對比

維度查表法 / Switch-CaseState 模式
新增狀態修改核心邏輯,風險高新增類別,零修改既有程式碼
狀態行為內聚性分散在條件分支封裝於各自類別
測試性需整合測試可單元測試每個狀態
記憶體較低每個狀態一個物件(可用單例)
適用場景狀態少、轉移固定狀態多、轉移複雜、頻繁擴充

進階議題

  • 狀態資料共享:Context 持有共享資料(如餘額),State 僅含行為。
  • 動態狀態建立:若狀態需帶參數,改用 Factory 建立,而非 Singleton。
  • 與 Strategy 模式差異:Strategy 封裝「演算法」,可自由切換;State 封裝「狀態行為」,切換由狀態內部控制。

總結

State 模式以「物件代表狀態」的優雅設計,將狀態轉移邏輯局部化,實現高內聚、低耦合、易擴充。下次面對複雜流程控制時,請先問自己:「這是不是一個有限狀態機?若是,State 模式可能是最佳解。」

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved