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
函數即可。
在上面的代碼中我們可以看到,雖然外部調用的是DiscountManager
的getDiscountByCustomer
接口,但是其實這個函數並沒有自己計算具體的折扣,而是委託其內部聚合的Startegy
對象來計算折扣。正式因爲我們在DiscountManager
中使用的是委託的方式,委託是一種弱關聯的方式,這使得我們可以很方便的整體替換折扣算法。
對比之前的Bridge
模式,我們會發現該兩者的實現方式很相似,都是通過委託的方式來實現功能,兩種模式的具體UML
類圖如下:
策略模式:
Bridge模式:
可以看到,兩個UML類圖的結構幾乎一樣,不同的地方就是,在Bridge模式中,在類的功能層次層次可能會增加新的子類。這與兩個模式的用途有關:
策略模式的目的在於能夠方便的、動態替換策略算法。而Bridge
模式主要的特點是,將類的功能層次和實現層次分離開,使得二者可以獨立的擴展。言下之意,在策略模式中,我們調用策略的Context
一般不會頻繁的更改,我們聚焦的是Strategy
類的替換,而在Bridge模式是爲了Abstraction
和Implementor
都可以很方便地擴展。