設計模式之策略模式

aoho發表於2019-02-28

策略模式的定義

策略模式屬於行為型模式。

strategydesign
策略模式

策略模式是對演算法的封裝,把一系列的演算法分別封裝到對應的類中,並且這些類實現相同的介面,相互之間可以替換。在策略模式中,呼叫演算法的主體則是封裝到了封裝類Context中,抽象策略Strategy一般是一個介面,目的只是為了定義規範,裡面一般不包含邏輯。其實,這只是通用實現,而在實際程式設計中,因為各個具體策略實現類之間難免存在一些相同的邏輯,為了避免重複的程式碼,我們常常使用抽象類來擔任Strategy的角色,在裡面封裝公共的程式碼。

策略模式的適用場景:如通知服務有三種通知方式:應用服務內部通知、簡訊和郵件三種方式。根據需要,可能使用三種方式中的任意一種。此種場景可以使用策略模式,客戶端根據需求進行相應的選擇。

策略模式的結構

  • 環境類:對策略進行二次封裝,目的是避免高層模組對策略的直接呼叫。
  • 抽象策略:通常情況下為一個介面,當各個實現類中存在著重複的邏輯時,則使用抽象類來封裝這部分公共的程式碼,此時,策略模式看上去更像是模版方法模式。
  • 具體策略:具體策略角色通常由一組封裝了演算法的類來擔任,這些類之間可以根據需要自由替換。

策略模式的優點:

  • 支援“開閉原則”,使用者可以在不修改原有系統的基礎上選擇演算法或行為,也可以靈活地增加新的演算法或行為,提供了管理相關的演算法族的辦法。
  • 可以避免使用多重條件語句。多重條件語句不易維護,它把採取哪一種演算法或採取哪一種行為的邏輯與演算法或行為的邏輯混合在一起,統統列在一個多重條件語句裡面,比使用繼承的辦法還要原始和落後。

策略模式的缺點:

  • 必須對客戶端(呼叫者)暴露所有的策略類,因為使用哪種策略是由客戶端來決定的,因此,客戶端應該知道有什麼策略,並且瞭解各種策略之間的區別,否則,後果很嚴重。比如,客戶端要使用一個容器,有連結串列實現的,也有陣列實現的,客戶端是不是也要明白連結串列和陣列有什麼區別?就這一點來說是有悖於迪米特法則的。
  • 由於策略模式把每個具體的策略實現都單獨封裝成為類,如果備選的策略很多的話,那麼物件的數目就會很可觀。

策略模式的實踐

通知介面,抽象策略類實現:

public interface Notify {
    // 策略方法
    public void notice();
}
複製程式碼

環境Context的實現:

public class Context {
    //持有一個具體策略的物件
    private Notify notify;
    // 建構函式,傳入一個具體策略物件
    public Context(Notify notify){
        this. notify = notify;
    }
    // 策略方法
    public void contextInterface(){
        notify.notice();
    }
}
複製程式碼

郵件通知策略類的實現:

public class MailNotify implements Notify {
    @Override
    public void notice() {
        //相關的業務
    }
}
複製程式碼

簡訊通知策略類的實現:

public class SMSNotify implements Notify {
    @Override
    public void notice() {
        //相關的業務
    }
}
複製程式碼

客戶端進行呼叫簡訊通知策略:

public class Client {
    public static void main(String[] args) {
        //選擇並建立需要使用的策略物件
        Notify strategy = new SMSNotify();
        //建立環境
        Context context = new Context(strategy);
        //通過環境Context進行呼叫
        context. contextInterface();
    }
}
複製程式碼

總結

策略模式的重點不是如何實現演算法,而是如何組織、呼叫這些演算法,從而讓程式結構更靈活,具有更好的維護性和擴充套件性。執行期間,策略模式在每一個時刻只能使用一個具體的策略實現物件,雖然可以動態地在不同的策略實現中切換,但是同時只能使用一個。經常見到的是,所有的具體策略類都有一些公有的行為。這時候,就應當把這些公有的行為放到共同的抽象策略角色Strategy類裡面。當然這時候抽象策略角色必須要用Java抽象類實現,而不能使用介面。

適用場景:

  • 如果在一個系統裡面有許多類,它們之間的區別僅在於它們的行為,那麼使用策略模式可以動態地讓一個物件在許多行為中選擇一種行為。
  • 一個系統需要動態地在幾種演算法中選擇一種。
  • 如果一個物件有很多的行為,如果不用恰當的模式,這些行為就只好使用多重的條件選擇語句來實現。

vs工廠模式

工廠模式是建立型模式 ,它關注物件建立,提供建立物件的介面. 讓物件的建立與具體的使用客戶無關。
策略模式是物件行為型模式 ,它關注行為和演算法的封裝 。它定義一系列的演算法,把每一個演算法封裝起來, 並且使它們可相互替換。使得演算法可獨立於使用它的客戶而變化。

vs模版方法模式

另一種模式也是關注對演算法的封裝——模版方法模式,對照類圖可以看到,策略模式與模版方法模式的區別僅僅是多了一個單獨的封裝類Context,它與模版方法模式的區別在於:在模版方法模式中,呼叫演算法的主體在抽象的父類中,而在策略模式中,呼叫演算法的主體則是封裝到了封裝類Context中,抽象策略Strategy一般是一個介面,目的只是為了定義規範,裡面一般不包含邏輯。其實,這只是通用實現,而在實際程式設計中,因為各個具體策略實現類之間難免存在一些相同的邏輯,為了避免重複的程式碼,我們常常使用抽象類來擔任Strategy的角色,在裡面封裝公共的程式碼,因此,在很多應用的場景中,在策略模式中一般會看到模版方法模式的影子。

訂閱最新文章,歡迎關注我的公眾號

微信公眾號

參考

  1. 23種設計模式(12):策略模式
  2. 設計模式 ( 十八 ) 策略模式Strategy(物件行為型)

相關文章