State Pattern(狀態模式)

是什麼?

State Pattern 讓物件在內部狀態改變時改變它的行為。每個狀態被封裝成獨立的類別,物件把行為委派給當前的狀態物件。從外部看,好像物件改變了它的類別。

ℹ️GoF 分類

State 屬於行為型模式(Behavioral Pattern),重點在於用物件組合取代大量的 if-else 狀態判斷。

什麼時候用?

用以下 if/then 條件判斷:

什麼時候不該用?

⚠️過度設計警告

如果狀態只有 2-3 個且不太可能增加,用簡單的 enum + switch 就夠了。State Pattern 適合狀態多、轉換邏輯複雜、且狀態會擴增的場景。每個狀態都要建一個類別,簡單場景反而增加複雜度。

執行流程

1

定義 State 介面

宣告所有狀態共用的行為方法

2

實作具體 State

每個狀態類別實作該狀態下的行為

3

建立 Context

持有當前狀態的參考,委派行為給狀態

4

狀態轉換

在狀態類別內部決定下一個狀態

5

Client 操作

Client 只跟 Context 互動,不需要知道當前狀態

流程解讀:State 的精髓是「狀態類別自管轉換」。每個 ConcreteState 知道自己在哪些操作下可以轉到哪個狀態,也知道哪些操作在當前狀態下是非法的。Context 只做委派,不包含任何狀態邏輯。這讓新增狀態時只需新增一個類別並定義它的行為和轉換規則,不需要修改 Context 或其他狀態。

程式碼範例

C# 版本

csharp
// 1. State 介面
public interface IOrderState
{
    void Next(Order order);
    void Cancel(Order order);
    string GetStatus();
}
 
// 2. 具體狀態
public class PendingState : IOrderState
{
    public void Next(Order order)
    {
        Console.WriteLine("訂單已確認,開始處理");
        order.SetState(new ProcessingState());
    }
 
    public void Cancel(Order order)
    {
        Console.WriteLine("訂單已取消");
        order.SetState(new CancelledState());
    }
 
    public string GetStatus() => "待確認";
}
 
public class ProcessingState : IOrderState
{
    public void Next(Order order)
    {
        Console.WriteLine("訂單已出貨");
        order.SetState(new ShippedState());
    }
 
    public void Cancel(Order order)
    {
        Console.WriteLine("處理中的訂單無法取消,請聯繫客服");
    }
 
    public string GetStatus() => "處理中";
}
 
public class ShippedState : IOrderState
{
    public void Next(Order order)
    {
        Console.WriteLine("訂單已送達,交易完成");
        order.SetState(new CompletedState());
    }
 
    public void Cancel(Order order)
    {
        Console.WriteLine("已出貨無法取消,請申請退貨");
    }
 
    public string GetStatus() => "已出貨";
}
 
public class CompletedState : IOrderState
{
    public void Next(Order order) => Console.WriteLine("訂單已完成,無法繼續");
    public void Cancel(Order order) => Console.WriteLine("已完成訂單無法取消");
    public string GetStatus() => "已完成";
}
 
public class CancelledState : IOrderState
{
    public void Next(Order order) => Console.WriteLine("已取消訂單無法繼續");
    public void Cancel(Order order) => Console.WriteLine("訂單已經取消了");
    public string GetStatus() => "已取消";
}
 
// 3. Context
public class Order
{
    private IOrderState _state;
 
    public Order() => _state = new PendingState();
 
    public void SetState(IOrderState state) => _state = state;
    public void Next() => _state.Next(this);
    public void Cancel() => _state.Cancel(this);
    public string GetStatus() => _state.GetStatus();
}
 
// 4. 使用
var order = new Order();
Console.WriteLine($"狀態:{order.GetStatus()}"); // 待確認
order.Next();   // 訂單已確認,開始處理
order.Next();   // 訂單已出貨
order.Cancel(); // 已出貨無法取消,請申請退貨
order.Next();   // 訂單已送達,交易完成

TypeScript 版本

typescript
// 1. State 介面
interface OrderState {
  next(order: Order): void;
  cancel(order: Order): void;
  getStatus(): string;
}
 
// 2. 具體狀態
class PendingState implements OrderState {
  next(order: Order) {
    console.log("訂單已確認,開始處理");
    order.setState(new ProcessingState());
  }
  cancel(order: Order) {
    console.log("訂單已取消");
    order.setState(new CancelledState());
  }
  getStatus() { return "待確認"; }
}
 
class ProcessingState implements OrderState {
  next(order: Order) {
    console.log("訂單已出貨");
    order.setState(new ShippedState());
  }
  cancel(order: Order) { console.log("處理中的訂單無法取消"); }
  getStatus() { return "處理中"; }
}
 
class ShippedState implements OrderState {
  next(order: Order) {
    console.log("訂單已送達,交易完成");
    order.setState(new CompletedState());
  }
  cancel(order: Order) { console.log("已出貨無法取消"); }
  getStatus() { return "已出貨"; }
}
 
class CompletedState implements OrderState {
  next(_order: Order) { console.log("訂單已完成"); }
  cancel(_order: Order) { console.log("已完成無法取消"); }
  getStatus() { return "已完成"; }
}
 
class CancelledState implements OrderState {
  next(_order: Order) { console.log("已取消無法繼續"); }
  cancel(_order: Order) { console.log("已經取消了"); }
  getStatus() { return "已取消"; }
}
 
// 3. Context
class Order {
  private state: OrderState = new PendingState();
 
  setState(state: OrderState) { this.state = state; }
  next() { this.state.next(this); }
  cancel() { this.state.cancel(this); }
  getStatus() { return this.state.getStatus(); }
}
 
// 4. 使用
const order = new Order();
console.log(`狀態:${order.getStatus()}`); // 待確認
order.next();   // 訂單已確認,開始處理
order.next();   // 訂單已出貨
order.next();   // 訂單已送達,交易完成

Python 版本

python
from abc import ABC, abstractmethod
 
# 1. State 介面
class OrderState(ABC):
    @abstractmethod
    def next(self, order: "Order") -> None:
        pass
    @abstractmethod
    def cancel(self, order: "Order") -> None:
        pass
    @abstractmethod
    def get_status(self) -> str:
        pass
 
# 2. 具體狀態
class PendingState(OrderState):
    def next(self, order: "Order") -> None:
        print("訂單已確認,開始處理")
        order.set_state(ProcessingState())
 
    def cancel(self, order: "Order") -> None:
        print("訂單已取消")
        order.set_state(CancelledState())
 
    def get_status(self) -> str:
        return "待確認"
 
class ProcessingState(OrderState):
    def next(self, order: "Order") -> None:
        print("訂單已出貨")
        order.set_state(ShippedState())
 
    def cancel(self, order: "Order") -> None:
        print("處理中的訂單無法取消")
 
    def get_status(self) -> str:
        return "處理中"
 
class ShippedState(OrderState):
    def next(self, order: "Order") -> None:
        print("訂單已送達,交易完成")
        order.set_state(CompletedState())
 
    def cancel(self, order: "Order") -> None:
        print("已出貨無法取消")
 
    def get_status(self) -> str:
        return "已出貨"
 
class CompletedState(OrderState):
    def next(self, order: "Order") -> None:
        print("訂單已完成")
    def cancel(self, order: "Order") -> None:
        print("已完成無法取消")
    def get_status(self) -> str:
        return "已完成"
 
class CancelledState(OrderState):
    def next(self, order: "Order") -> None:
        print("已取消無法繼續")
    def cancel(self, order: "Order") -> None:
        print("已經取消了")
    def get_status(self) -> str:
        return "已取消"
 
# 3. Context
class Order:
    def __init__(self):
        self._state: OrderState = PendingState()
 
    def set_state(self, state: OrderState) -> None:
        self._state = state
 
    def next(self) -> None:
        self._state.next(self)
 
    def cancel(self) -> None:
        self._state.cancel(self)
 
    def get_status(self) -> str:
        return self._state.get_status()
 
# 4. 使用
order = Order()
print(f"狀態:{order.get_status()}")  # 待確認
order.next()    # 訂單已確認,開始處理
order.next()    # 訂單已出貨
order.next()    # 訂單已送達,交易完成

Java 版本

java
// 1. State 介面
public interface OrderState {
    void next(Order order);
    void cancel(Order order);
    String getStatus();
}
 
// 2. 具體狀態
public class PendingState implements OrderState {
    public void next(Order order) {
        System.out.println("訂單已確認,開始處理");
        order.setState(new ProcessingState());
    }
    public void cancel(Order order) {
        System.out.println("訂單已取消");
        order.setState(new CancelledState());
    }
    public String getStatus() { return "待確認"; }
}
 
public class ProcessingState implements OrderState {
    public void next(Order order) {
        System.out.println("訂單已出貨");
        order.setState(new ShippedState());
    }
    public void cancel(Order order) {
        System.out.println("處理中的訂單無法取消");
    }
    public String getStatus() { return "處理中"; }
}
 
public class ShippedState implements OrderState {
    public void next(Order order) {
        System.out.println("訂單已送達,交易完成");
        order.setState(new CompletedState());
    }
    public void cancel(Order order) {
        System.out.println("已出貨無法取消");
    }
    public String getStatus() { return "已出貨"; }
}
 
public class CompletedState implements OrderState {
    public void next(Order order) { System.out.println("訂單已完成"); }
    public void cancel(Order order) { System.out.println("已完成無法取消"); }
    public String getStatus() { return "已完成"; }
}
 
public class CancelledState implements OrderState {
    public void next(Order order) { System.out.println("已取消無法繼續"); }
    public void cancel(Order order) { System.out.println("已經取消了"); }
    public String getStatus() { return "已取消"; }
}
 
// 3. Context
public class Order {
    private OrderState state = new PendingState();
 
    public void setState(OrderState state) { this.state = state; }
    public void next() { state.next(this); }
    public void cancel() { state.cancel(this); }
    public String getStatus() { return state.getStatus(); }
}
 
// 4. 使用
Order order = new Order();
System.out.println("狀態:" + order.getStatus()); // 待確認
order.next();   // 訂單已確認,開始處理
order.next();   // 訂單已出貨
order.next();   // 訂單已送達,交易完成

結構圖

Client
uses
Context (Order)
delegates to
State Interface
ConcreteState A (Pending)
implements
ConcreteState B (Processing)
implements
ConcreteState C (Shipped)

結構解讀:Client 只跟 Context(Order)互動,不直接操作 State。Context 持有當前 State 的參考,將行為委派給它。每個 ConcreteState 知道自己的合法轉換(例如 Pending 可以轉到 Processing 或 Cancelled),在方法內部呼叫 context.SetState(new NextState()) 完成轉換。非法操作會被當前狀態拒絕(如已出貨不能取消)。

實戰補充

💡資深開發者經驗

訂單狀態機:電商系統的訂單流程是 State Pattern 最經典的應用場景。每個狀態有明確的合法轉換,非法操作會被拒絕。

遊戲角色狀態:RPG 遊戲中角色的「正常」「中毒」「暈眩」「無敵」狀態,不同狀態下攻擊、防禦、移動的行為都不同。

State vs Strategy:State Pattern 的狀態之間知道彼此的存在(狀態 A 知道要切換到狀態 B),Strategy 的策略之間互不相識。State 強調狀態轉換,Strategy 強調演算法替換。

工作流程引擎:審批流程、CI/CD Pipeline、文件審核流程,都可以用 State Pattern 來管理。搭配持久化(存到資料庫),可以實現跨 Session 的長期狀態管理。

理解測驗

🤔 State Pattern 和大量 if-else 相比,最大的優勢是什麼?

🤔 State Pattern 中,狀態轉換的邏輯通常放在哪裡?

🤔 State Pattern 和 Strategy Pattern 最關鍵的差異是什麼?

面試常見問題

Q: State Pattern 和 Strategy Pattern 結構幾乎一樣,怎麼區分?

A: 三個關鍵差異:(1) 狀態之間的認知 — State 的各狀態知道彼此存在且會自動轉換,Strategy 的各策略互不相識。(2) 切換控制 — State 由狀態物件自己決定轉換,Strategy 由 Client 決定替換。(3) 語意 — State 表達「物件在不同生命階段的行為」,Strategy 表達「同一個操作的不同演算法」。

Q: 狀態轉換邏輯放在 State 裡面好還是 Context 裡面好?

A: 放在 State 裡面(如範例)讓每個狀態自管轉換,新增狀態不影響其他狀態,但狀態之間產生了依賴。放在 Context 裡面讓所有轉換邏輯集中管理,容易看到全貌,但 Context 會膨脹。實務建議:狀態數量少且轉換規則簡單 → 放 State 內部;狀態多且轉換複雜 → 抽出獨立的 State Machine 配置。

相關模式

| 模式 | 關係 | |------|------| | Strategy | 結構幾乎相同,但意圖不同。State 的狀態會自動轉換,Strategy 的策略由 Client 選擇。口訣:「State 自己換,Strategy 別人換」 | | Singleton / Flyweight | 無狀態的 ConcreteState(如只有行為沒有欄位)可以用 Singleton 或 Flyweight 共享,避免每次轉換都 new | | Command | 可搭配使用 — State 決定「能做什麼」,Command 封裝「怎麼做」 | | Mediator | 狀態機的轉換邏輯複雜時,可以用 Mediator 來集中管理狀態之間的轉換規則 |

重點整理

💡一句話記住

State Pattern = 紅綠燈:狀態自動切換,行為跟著變。 口訣:「狀態管行為,轉換靠自己」

| 概念 | 說明 | |------|------| | State(狀態介面) | 定義所有狀態共用的行為方法 | | ConcreteState(具體狀態) | 實作該狀態下的行為和轉換邏輯 | | Context(上下文) | 持有當前狀態,委派行為給狀態物件 | | 核心好處 | 消除 if-else 狀態判斷,新增狀態符合開放封閉原則 | | 代價 | 狀態少時過度設計,狀態類別數量會增加 |

你可能也想看

Iterator PatternMediator Pattern

按 ← → 鍵切換課程