[設計模式] - No.12 Strategy模式

Strategy模式

Strategy模式又稱策略模式,在策略模式中,我們將特定的算法封裝起來,從而實現整體地替換算法的功能,以便我們能夠按照不同的算法去解決同一個問題。《圖解設計模式》這本書中的,策略模式這一章提供的代碼示例有些冗長,所以我參考了這篇文章,編寫了一個簡單的例子,用較少的代碼解釋什麼是策略模式。

假設我們有一個場景,在商場中針對不同的客戶的身份,提供不同的優惠方案,最簡單的一種實現如下所示:

public class MarketPrice {
    private final String OLD_CUSTOME = "老客戶";
    private final String NEW_CUSTOME = "新客戶";
    private final String VIP_CUSTOME = "VIP客戶";
    public double getDiscount(String custome){
        double discount = 1.0;
        if(OLD_CUSTOME.equals(custome)){
            System.out.println("老客戶有9折優惠!");
            discount = 0.9;
        }
        else if(NEW_CUSTOME.equals(custome)){
            System.out.println("新客戶沒有優惠!");
        }
        else if (VIP_CUSTOME.equals(custome)){
            System.out.println("VIP客戶有8優惠!");
            discount = 0.8;
        }
        return discount;
    }
}

這段代碼看起來沒有什麼問題,並且在現有的工程中,很多時候我們也是這樣做的。但是,一旦我們希望對現有代碼進行擴展,例如,增加一個針對SVIP的優惠信息,我們就必須對MarketPrice進行擴展,在if-else中增加新的語句,這又違反了開閉原則。

在設計模式的學習中,我們發現SOLID原則中,最重要的一個就是開閉原則,其他的原則基本上是爲了開閉原則服務的。由於我們希望遵循開閉原則,對修改關閉,對擴展開放,所以我們才希望將抽象和實現分離開,才需要使用里氏替換原則,才需要依賴反轉,最終達到解耦的目的。

重新回到代碼,我們將MarketPrice中的算法抽離出來,封裝成獨立的算法,當我們需要不同的優惠策略的時候,只需要替換算法即可。

public interface Strategy {
    public double getDiscount();
}
public class OldCustomerStrategy implements Strategy{
    @Override
    public double getDiscount() {
        System.out.println("老客戶有9折優惠!");
        return 0.9;
    }
}
public class VIPCustomerStrategy implements Strategy {
    @Override
    public double getDiscount() {
        System.out.println("VIP客戶有8優惠!");
        return 0.8;
    }
}
public class DiscountManager {
    private Strategy strategy;

    public DiscountManager(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public double getDiscountByCustomer(){
        return strategy.getDiscount();
    }

}

在主程序中,我們這樣調用:

public class Main {
    public static void main(String args []){
        double product = 2000;

        DiscountManager manager = new DiscountManager(new VIPCustomerStrategy());

        double discount4 = manager.getDiscountByCustomer();
        System.out.println(String.format("VIP客戶實際購買價 $%.2f", product*discount4));

        manager.setStrategy(new OldCustomerStrategy());

        double discount5 = manager.getDiscountByCustomer();
        System.out.println(String.format("老客戶實際購買價 $%.2f", product*discount5));

    }
}

如果我們希望增加一個針對SVIP的優惠策略,只需要在編寫一個新的類,繼承Strategy並複寫其getDiscount函數即可。

在上面的代碼中我們可以看到,雖然外部調用的是DiscountManagergetDiscountByCustomer接口,但是其實這個函數並沒有自己計算具體的折扣,而是委託其內部聚合的Startegy對象來計算折扣。正式因爲我們在DiscountManager中使用的是委託的方式,委託是一種弱關聯的方式,這使得我們可以很方便的整體替換折扣算法。

對比之前的Bridge模式,我們會發現該兩者的實現方式很相似,都是通過委託的方式來實現功能,兩種模式的具體UML類圖如下:

策略模式:

在這裏插入圖片描述

Bridge模式:
在這裏插入圖片描述

可以看到,兩個UML類圖的結構幾乎一樣,不同的地方就是,在Bridge模式中,在類的功能層次層次可能會增加新的子類。這與兩個模式的用途有關:

策略模式的目的在於能夠方便的、動態替換策略算法。而Bridge模式主要的特點是,將類的功能層次和實現層次分離開,使得二者可以獨立的擴展。言下之意,在策略模式中,我們調用策略的Context一般不會頻繁的更改,我們聚焦的是Strategy類的替換,而在Bridge模式是爲了AbstractionImplementor都可以很方便地擴展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章