06_Strategy Pattern 策略模式

Gf_Shen發表於2020-09-30

GoF 定義: 定義一個系列的演算法,然後封裝每個演算法,使得他們之間可以互換。策略模式使得演算法在客戶端之間可以獨立地變化。(Define a family of algorithms, encapsulate each one, and make them interchangeable. The strategy pattern lets the algorithm vary independently from client to client.)

進一步解釋:策略模式是把演算法封裝成相同的介面,然後在執行時按需動態呼叫。一旦有了統一介面,客戶端就可用用同一份程式碼處理不同的情況,要做的只是在此之前設定不同的演算法子類。

例如,有一個段子講的是:“一個標準的程式設計師在睡覺時候都會準備兩個杯子,一個是空的,一個裝滿了水。裝滿水的杯子為的是醒來之後口渴,空白杯子是為了萬一醒來之後不渴。”這位哥們一看就是學過設計模式的科班出身,把策略模式用到了生活中。

我們用程式碼表述這個情景:

import java.util.Random;

/**
 * strategy pattern
 */
public class Main {
    public static void main(String[] args) {
        String[] conditionArray = {"Thirsty", "NotThirsty"};
        // 例項化兩個演算法類
        IPickCup whenThirsty = new PickCupWhenThirsty();
        IPickCup whenNotThirsty = new PickCupWhenNotThirsty();
        WakeUp wakeUp = new WakeUp();
        for (int i = 0; i < 10; i++) {
            // 迴圈10次,隨機生成口渴和不渴的情形
            String condition = conditionArray[new Random().nextInt(2)];
            System.out.println("\nTime "+ i + " I'm "+condition);
            // 根據情況傳入不同的演算法
            if (condition.equals("Thirsty")){
                wakeUp.setPickCup(whenThirsty);
            } else {
                wakeUp.setPickCup(whenNotThirsty);
            }
            wakeUp.doRegular();
        }
    }
}

// 喝水動作的介面函式
abstract class IPickCup {
    abstract void pickUpCup();
}

// 感覺口渴時
class PickCupWhenThirsty extends IPickCup {
    @Override
    void pickUpCup() {
        System.out.println("Pick up the cup");
        System.out.println("Drink...");
        System.out.println("Put down the cup");
        System.out.println("And continue go to sleep");
    }
}

// 萬一不渴
class PickCupWhenNotThirsty extends IPickCup {
    @Override
    void pickUpCup() {
        System.out.println("pick up the cup");
        System.out.println("Put down the cup");
        System.out.println("And continue go to sleep");
    }
}

class WakeUp {
    IPickCup pickCup = null;

    // 在這裡動態載入不同的演算法(即渴不渴的情況)
    void setPickCup(IPickCup pickCup) {
        this.pickCup = pickCup;
    }

    // 策略模式可以使用同一份程式碼處理不同的情況
    void doRegular() {
        System.out.println("I am waking up");
        pickCup.pickUpCup();
    }
}

類圖:

輸出:

策略模式的關鍵在於實現具有統一介面的演算法類(這裡的杯子就是介面,因此就算沒有水,杯子還是要有的),然後用不同的演算法類處理不同的情形(口渴和不口渴)。在呼叫過程中,客戶端程式根據不同情形動態載入不同的演算法(WakeUp 類中的setPickCup()方法)。

 

相關文章