cover

你看過這種 code 嗎?

if (order.status === 'pending') { /* ... */ }
else if (order.status === 'processing') { /* ... */ }
else if (order.status === 'shipped') { /* ... */ }
else if (order.status === 'completed') { /* ... */ }

而且這段 code 出現在五個不同的地方。每次新增一個狀態,你就要改五處。改漏一處?恭喜,出 bug 了。

先講結論

State Pattern 把每個狀態的行為封裝成獨立的 class。物件不再用 if-else 判斷「我現在是什麼狀態」,而是直接委託給當前狀態物件處理。狀態自己知道自己能做什麼、不能做什麼,也知道下一步該切換到哪個狀態。

classDiagram
    class Order {
        -state : OrderState
        +setState(state) void
        +process() void
        +ship() void
        +complete() void
    }
    class OrderState {
        <<interface>>
        +process()* void
        +ship()* void
        +complete()* void
    }
    class PendingState {
        +process() void
        +ship() void
        +complete() void
    }
    class ProcessingState {
        +process() void
        +ship() void
        +complete() void
    }
    class ShippedState {
        +process() void
        +ship() void
        +complete() void
    }
    Order o-- OrderState : 持有當前狀態
    OrderState <|.. PendingState
    OrderState <|.. ProcessingState
    OrderState <|.. ShippedState

實戰:訂單狀態流轉

State 模式:訂單狀態流轉

class Order {
    constructor() {
        this.state = new PendingState(this);
    }
    setState(state) { this.state = state; }
    process() { this.state.process(); }
    ship() { this.state.ship(); }
    complete() { this.state.complete(); }
}
 
class PendingState {
    constructor(order) { this.order = order; }
    process() {
        console.log('開始處理訂單...');
        this.order.setState(new ProcessingState(this.order));
    }
    ship() { console.log('還沒處理,不能出貨'); }
    complete() { console.log('還沒出貨,不能完成'); }
}
 
class ProcessingState {
    constructor(order) { this.order = order; }
    process() { console.log('已經在處理了'); }
    ship() {
        console.log('訂單出貨中...');
        this.order.setState(new ShippedState(this.order));
    }
    complete() { console.log('還沒出貨,不能完成'); }
}
 
class ShippedState {
    constructor(order) { this.order = order; }
    process() { console.log('已經出貨了'); }
    ship() { console.log('已經出貨了'); }
    complete() {
        console.log('訂單完成!');
        // 可以切換到 CompletedState
    }
}
 
const order = new Order();
order.ship();      // 還沒處理,不能出貨
order.process();   // 開始處理訂單...
order.ship();      // 訂單出貨中...
order.complete();  // 訂單完成!

注意 order.ship() 在 Pending 狀態下會被擋掉——不用寫 if-else,PendingState 自己就會處理。

State vs Strategy:到底差在哪?

結構幾乎一模一樣,但意圖完全不同:

  • State:狀態由內部自動切換(「訂單處理完了,自動變成 shipped」)
  • Strategy:策略由外部主動選擇(「這次用黑五折扣」)

如果你的物件會「自己變」,用 State。如果是「別人幫它換」,用 Strategy。


State Pattern 就像交通號誌——紅燈的時候不能走、綠燈的時候可以走,燈號自己會換。只是有些人紅燈也照走不誤


延伸閱讀