You are currently viewing 策略模式:你的萬能武器庫

策略模式:你的萬能武器庫

  • 想像一下,你正在開發一款冒險遊戲。我們的主角是一位名叫「阿代」的勇者。阿代遇到怪獸時需要攻擊,但他有很多種攻擊方式。
  • 第一階段:混亂的菜鳥寫法(沒有使用模式 )
    • 阿代: 「如果我手上拿著劍,我就砍;如果我手上拿著弓箭,我就射;如果我手上拿著魔杖,我就丟火球……」
    • 在程式碼裡,這就像是一個超級長的 if-elseswitch 語句:
    • 如果 (武器 == 劍) { 執行砍擊動作(); } 否則 如果 (武器 == 弓箭) { 執行射擊動作(); } 否則 如果 (武器 == 魔杖) { 執行魔法動作(); } … (後面還有幾百種武器)
    • 這有什麼問題?
      • 阿代太累了:每次你要在這個遊戲裡加一把新武器(比如「平底鍋」),你就得把阿代的身體(程式碼)切開,塞進一個新的 else if。這很容易把阿代弄壞(產生 Bug)。
      • 邏輯臃腫:阿代的程式碼會變得越來越長,最後變成一團沒人看得懂的義大利麵。
  • 第二階段:策略模式登場(換個思維)
    • 這時候,策略模式就像是一個聰明的裝備大師,它走過來告訴你:
    • 「嘿!阿代不需要知道『怎麼揮劍』或『怎麼射箭』。阿代只需要知道他手上有一個攻擊按鈕,至於按下去會發生什麼,由他手上的武器決定。」
    • 這就是策略模式的核心:把「做什麼(攻擊)」和「怎麼做(揮劍、射箭)」分開。
    • 我們可以把這個模式拆解成三個簡單的角色:
    • 1. 介面 (The Interface) —— 也就是「通用插座」
      • 我們先定義一個規則,所有的武器都必須遵守這個規則。比如,所有武器都必須有一個功能叫做 attack()
        • 比喻: USB 插孔。不管你插的是滑鼠還是鍵盤,接頭形狀必須是一樣的。
    • 2. 具體策略 (Concrete Strategy) —— 也就是「各種武器」
      • 我們把每一種攻擊方式都做成一個獨立的小零件。
        • 寶劍策略:實現 attack()→效果是「揮砍」。
        • 弓箭策略:實現 attack()→效果是「射擊」。
        • 平底鍋策略:實現 attack()→效果是「敲暈敵人」。
        • 比喻: 這些就是你背包裡的各種裝備。
    • 3. 情境 (Context) —— 也就是「勇者阿代」
      • 阿代身上有一個「武器插槽」。他不在乎插上去的是什麼,他只管按攻擊鍵。
        • 比喻: 阿代是一台遊戲主機,武器是遊戲卡帶。換卡帶就能玩不同遊戲,不用拆主機。
  • 來看一點點超級簡單的程式碼(偽代碼)
    • 現在的邏輯變成了這樣:
    • 第一步:定義插座(介面)
interface 武器 {
void 攻擊(); // 每個武器都要會攻擊
}

第二步:製造武器(具體策略)

class 寶劍 implements 武器 {
void 攻擊() { print("帥氣地揮砍!"); }
}

class 弓箭 implements 武器 {
void 攻擊() { print("遠距離射擊!"); }
}

第三步:勇者阿代(情境)

class 勇者 {
武器 我的武器; // 阿代手上有個武器欄位

void 設定武器(武器 新武器) {
this.我的武器 = 新武器; // 這裡就是「換裝備」的動作
}

void 戰鬥() {
我的武器.攻擊(); // 阿代:我不管手裡是啥,反正給我打就對了!
}
}
  • 🎉 結局:發生了什麼改變?
    • 有一天,遊戲策劃說:「我們要加一個隱藏神武——鹹魚!」
      • 以前(沒有策略模式):你得打開「勇者阿代」的程式碼,在那個幾千行的 if-else 裡面小心翼翼地加一行「如果是鹹魚,就甩尾」。一旦改錯,阿代可能連劍都不會用了。
      • 現在(有策略模式)
        • 你完全不用碰阿代的程式碼。
        • 你只要新建一個檔案 鹹魚.class
        • 在遊戲裡呼叫 阿代.設定武器(new 鹹魚())
        • 搞定!
  • 📚 總結:策略模式的口訣
    • 「把變化的東西(策略),從不變的東西(情境)裡拿出來,包裝成獨立的零件,讓它們可以隨時替換。」
    • 就像換手機殼、換電池、或是換遊戲裝備一樣自然。這就是策略模式的精髓!

完整程式碼如下

from abc import ABC, abstractmethod

# ---------------------------------------------------------
# 1. The Strategy Interface (The "Universal Socket")
# ---------------------------------------------------------
class WeaponStrategy(ABC):
    """
    The interface that all weapons must implement.
    This ensures every weapon has an 'attack' method.
    """

    @abstractmethod
    def attack(self):
        # Every concrete weapon must implement this method
        pass


# ---------------------------------------------------------
# 2. Concrete Strategies (The "Weapons")
# ---------------------------------------------------------
class Sword(WeaponStrategy):
    def attack(self):
        # Implementation for sword attack
        print(">> Slash! The hero strikes with a sword.")

class Bow(WeaponStrategy):
    def attack(self):
        # Implementation for bow attack
        print(">> Whoosh! The hero shoots an arrow from a distance.")

class FryingPan(WeaponStrategy):
    def attack(self):
        # Implementation for the funny weapon
        print(">> Bonk! The hero hits the enemy with a frying pan.")


# ---------------------------------------------------------
# 3. The Context (The "Hero")
# ---------------------------------------------------------
class Hero:
    def __init__(self, name, weapon: WeaponStrategy):
        """
        The Hero is initialized with a name and a default weapon.
        :param weapon: An object that follows the WeaponStrategy interface
        """
        self.name = name
        self._weapon = weapon 

    def set_weapon(self, weapon: WeaponStrategy):
        """
        Allows the hero to switch weapons (strategies) at runtime.
        """
        print(f"\n[{self.name} is switching weapon...]")
        self._weapon = weapon

    def fight(self):
        """
        The hero delegates the attack logic to the weapon object.
        The hero doesn't know HOW to attack, just calls the method.
        """
        print(f"{self.name} starts fighting:")
        self._weapon.attack()


# ---------------------------------------------------------
# Client Code (The "Game Logic")
# ---------------------------------------------------------
if __name__ == "__main__":
    # 1. Create a hero named "Ardai" starting with a Sword
    hero = Hero("Ardai", Sword())

    # Ardai fights with the sword
    hero.fight()

    # 2. Strategy Pattern in action: Switch to a Bow dynamically
    hero.set_weapon(Bow())
    hero.fight()

    # 3. Switch to a Frying Pan
    hero.set_weapon(FryingPan())
    hero.fight()
0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments