1. 程式人生 > >[設計模式] - No.12 Strategy模式

[設計模式] - 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都可以很方便地擴充套件。