cover

如果你的 code 裡有一坨 if-elseswitch 在決定「這次要用哪種算法」,那你其實已經在做 Strategy Pattern 了——只是做得很醜而已。

先講結論

Strategy Pattern 的精髓是:把會變的行為抽出去,讓它可以被替換。Context 不管你用哪種策略,它只管呼叫 execute()。新增策略?寫一個新 class 就好,舊的完全不用動。

classDiagram
    class Context {
        -strategy : Strategy
        +setStrategy(strategy) void
        +executeStrategy() any
    }
    class Strategy {
        <<interface>>
        +execute()* any
    }
    class ConcreteStrategyA {
        +execute() any
    }
    class ConcreteStrategyB {
        +execute() any
    }
    Context --> Strategy
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB

實戰:電商折扣系統

雙十一打八折、黑五打七折、會員日九折——你打算全寫在同一個 calculateDiscount 裡嗎?

// 策略們
class BlackFridayStrategy {
    applyDiscount(amount) {
        return amount * 0.7; // 七折
    }
}
 
class MemberDayStrategy {
    applyDiscount(amount) {
        return amount * 0.9; // 九折
    }
}
 
class NoDiscountStrategy {
    applyDiscount(amount) {
        return amount; // 原價,殘忍
    }
}
 
// Context
class PromotionContext {
    constructor(strategy) {
        this.strategy = strategy;
    }
 
    setStrategy(strategy) {
        this.strategy = strategy;
    }
 
    calculateDiscount(amount) {
        return this.strategy.applyDiscount(amount);
    }
}
 
// 使用
const context = new PromotionContext(new BlackFridayStrategy());
console.log(context.calculateDiscount(1000)); // 700
 
// 活動結束,切回原價
context.setStrategy(new NoDiscountStrategy());
console.log(context.calculateDiscount(1000)); // 1000

重點在於:加一個「春節特賣策略」?寫一個新 class 就好。PromotionContext 一行都不用改。

Strategy vs 一堆 if-else

做法新增策略時可讀性測試難度
if-else改原本的 function越來越長整坨一起測
Strategy新增一個 class每個策略獨立各自測各自的

跟 State Pattern 有什麼不同?

好問題。結構幾乎一樣,但意圖不同:

  • Strategy:你主動選擇要用哪個策略(「我要用黑五折扣」)
  • State:狀態自己決定下一步(「訂單從 pending 變成 processing」)

如果策略是由外部決定的,用 Strategy;如果行為隨內部狀態自動變化,用 State

小心過度設計

如果你只有兩種策略,而且未來也不太會增加——一個 if-else 就夠了,不需要硬套 pattern。設計模式是解決問題的,不是拿來寫在履歷上的。


Strategy Pattern 就像手機換殼——手機功能不變,但你可以隨心情換個外觀。只不過,如果你只有一個殼,那你其實不需要一個殼的收納架


延伸閱讀