java設計模式策略模式圖文示例詳解

策略模式

亦稱:Strategy

意圖

策略模式是一種行為設計模式,它能讓你定義一系列算法,並將每種算法分別放入獨立的類中,以使算法的對象能夠相互替換。

問題

一天,你打算為遊客們創建一款導遊程序。該程序的核心功能是提供美觀的地圖,以幫助用戶在任何城市中快速定位。

用戶期待的程序新功能是自動路線規劃:他們希望輸入地址後就能在地圖上看到前往目的地的最快路線。

程序的首個版本隻能規劃公路路線。駕車旅行的人們對此非常滿意。但很顯然,並非所有人都會在度假時開車。因此你在下次更新時添加瞭規劃步行路線的功能。此後,你又添加瞭規劃公共交通路線的功能。

而這隻是個開始。不久後,你又要為騎行者規劃路線。又過瞭一段時間,你又要為遊覽城市中的所有景點規劃路線。

導遊代碼將變得非常臃腫。

盡管從商業角度來看,這款應用非常成功,但其技術部分卻讓你非常頭疼:每次添加新的路線規劃算法後,導遊應用中主要類的體積就會增加一倍。終於在某個時候,你覺得自己沒法繼續維護這堆代碼瞭。

無論是修復簡單缺陷還是微調街道權重,對某個算法進行任何修改都會影響整個類,從而增加在已有正常運行代碼中引入錯誤的風險。

此外,團隊合作將變得低效。如果你在應用成功發佈後招募瞭團隊成員,他們會抱怨在合並沖突的工作上花費瞭太多時間。在實現新功能的過程中,你的團隊需要修改同一個巨大的類,這樣他們所編寫的代碼相互之間就可能會出現沖突。

解決方案

策略模式建議找出負責用許多不同方式完成特定任務的類,然後將其中的算法抽取到一組被稱為策略的獨立類中。

名為上下文的原始類必須包含一個成員變量來存儲對於每種策略的引用。上下文並不執行任務,而是將工作委派給已連接的策略對象。

上下文不負責選擇符合任務需要的算法——客戶端會將所需策略傳遞給上下文。實際上,上下文並不十分瞭解策略,它會通過同樣的通用接口與所有策略進行交互,而該接口隻需暴露一個方法來觸發所選策略中封裝的算法即可。

因此,上下文可獨立於具體策略。這樣你就可在不修改上下文代碼或其他策略的情況下添加新算法或修改已有算法瞭。

路線規劃策略。

在導遊應用中,每個路線規劃算法都可被抽取到隻有一個build­Route生成路線方法的獨立類中。該方法接收起點和終點作為參數,並返回路線中途點的集合。

即使傳遞給每個路徑規劃類的參數一模一樣,其所創建的路線也可能完全不同。主要導遊類的主要工作是在地圖上渲染一系列中途點,不會在意如何選擇算法。該類中還有一個用於切換當前路徑規劃策略的方法,因此客戶端(例如用戶界面中的按鈕)可用其他策略替換當前選擇的路徑規劃行為。

真實世界類比

各種前往機場的出行策略

假如你需要前往機場。你可以選擇乘坐公共汽車、預約出租車或騎自行車。這些就是你的出行策略。你可以根據預算或時間等因素來選擇其中一種策略。

策略模式結構

  • 上下文(Context)維護指向具體策略的引用,且僅通過策略接口與該對象進行交流。
  • 策略(Strategy)接口是所有具體策略的通用接口,它聲明瞭一個上下文用於執行策略的方法。
  • 具體策略(Concrete Strategies)實現瞭上下文所用算法的各種不同變體。
  • 當上下文需要運行算法時,它會在其已連接的策略對象上調用執行方法。上下文不清楚其所涉及的策略類型與算法的執行方式。
  • 客戶端(Client)會創建一個特定策略對象並將其傳遞給上下文。上下文則會提供一個設置器以便客戶端在運行時替換相關聯的策略。

偽代碼

在本例中,上下文使用瞭多個策略來執行不同的計算操作。

// 策略接口聲明瞭某個算法各個不同版本間所共有的操作。上下文會使用該接口來
// 調用有具體策略定義的算法。
interface Strategy is
    method execute(a, b)
// 具體策略會在遵循策略基礎接口的情況下實現算法。該接口實現瞭它們在上下文
// 中的互換性。
class ConcreteStrategyAdd implements Strategy is
    method execute(a, b) is
        return a + b
class ConcreteStrategySubtract implements Strategy is
    method execute(a, b) is
        return a - b
class ConcreteStrategyMultiply implements Strategy is
    method execute(a, b) is
        return a * b
// 上下文定義瞭客戶端關註的接口。
class Context is
    // 上下文會維護指向某個策略對象的引用。上下文不知曉策略的具體類。上下
    // 文必須通過策略接口來與所有策略進行交互。
    private strategy: Strategy
    // 上下文通常會通過構造函數來接收策略對象,同時還提供設置器以便在運行
    // 時切換策略。
    method setStrategy(Strategy strategy) is
        this.strategy = strategy
    // 上下文會將一些工作委派給策略對象,而不是自行實現不同版本的算法。
    method executeStrategy(int a, int b) is
        return strategy.execute(a, b)
// 客戶端代碼會選擇具體策略並將其傳遞給上下文。客戶端必須知曉策略之間的差
// 異,才能做出正確的選擇。
class ExampleApplication is
    method main() is
        創建上下文對象。
        讀取第一個數。
        讀取最後一個數。
        從用戶輸入中讀取期望進行的行為。
        if (action == addition) then
            context.setStrategy(new ConcreteStrategyAdd())
        if (action == subtraction) then
            context.setStrategy(new ConcreteStrategySubtract())
        if (action == multiplication) then
            context.setStrategy(new ConcreteStrategyMultiply())
        result = context.executeStrategy(First number, Second number)
        打印結果。

策略模式適合應用場景

當你想使用對象中各種不同的算法變體,並希望能在運行時切換算法時,可使用策略模式。

策略模式讓你能夠將對象關聯至可以不同方式執行特定子任務的不同子對象,從而以間接方式在運行時更改對象行為。

當你有許多僅在執行某些行為時略有不同的相似類時,可使用策略模式。

策略模式讓你能將不同行為抽取到一個獨立類層次結構中,並將原始類組合成同一個,從而減少重復代碼。

如果算法在上下文的邏輯中不是特別重要,使用該模式能將類的業務邏輯與其算法實現細節隔離開來。

策略模式讓你能將各種算法的代碼、內部數據和依賴關系與其他代碼隔離開來。不同客戶端可通過一個簡單接口執行算法,並能在運行時進行切換。

當類中使用瞭復雜條件運算符以在同一算法的不同變體中切換時,可使用該模式。

策略模式將所有繼承自同樣接口的算法抽取到獨立類中,因此不再需要條件語句。原始對象並不實現所有算法的變體,而是將執行工作委派給其中的一個獨立算法對象。

實現方式

  • 從上下文類中找出修改頻率較高的算法(也可能是用於在運行時選擇某個算法變體的復雜條件運算符)。
  • 聲明該算法所有變體的通用策略接口。
  • 將算法逐一抽取到各自的類中,它們都必須實現策略接口。
  • 在上下文類中添加一個成員變量用於保存對於策略對象的引用。然後提供設置器以修改該成員變量。上下文僅可通過策略接口同策略對象進行交互,如有需要還可定義一個接口來讓策略訪問其數據。
  • 客戶端必須將上下文類與相應策略進行關聯,使上下文可以預期的方式完成其主要工作。

策略模式優缺點

  • 你可以在運行時切換對象內的算法。
  • 你可以將算法的實現和使用算法的代碼隔離開來。
  • 你可以使用組合來代替繼承。
  • 開閉原則。你無需對上下文進行修改就能夠引入新的策略。

策略模式優缺點

  • 如果你的算法極少發生改變,那麼沒有任何理由引入新的類和接口。使用該模式隻會讓程序過於復雜。
  • 客戶端必須知曉策略間的不同——它需要選擇合適的策略。
  • 許多現代編程語言支持函數類型功能,允許你在一組匿名函數中實現不同版本的算法。這樣,你使用這些函數的方式就和使用策略對象時完全相同,無需借助額外的類和接口來保持代碼簡潔。

與其他模式的關系

橋接模式、狀態模式和策略模式(在某種程度上包括適配器模式)模式的接口非常相似。實際上,它們都基於組合模式——即將工作委派給其他對象,不過也各自解決瞭不同的問題。模式並不隻是以特定方式組織代碼的配方,你還可以使用它們來和其他開發者討論模式所解決的問題。

命令模式和策略看上去很像,因為兩者都能通過某些行為來參數化對象。但是,它們的意圖有非常大的不同。

  • 你可以使用命令來將任何操作轉換為對象。操作的參數將成為對象的成員變量。你可以通過轉換來延遲操作的執行、將操作放入隊列、保存歷史命令或者向遠程服務發送命令等。
  • 另一方面,策略通常可用於描述完成某件事的不同方式,讓你能夠在同一個上下文類中切換算法。

裝飾模式可讓你更改對象的外表,策略則讓你能夠改變其本質。

模板方法模式基於繼承機制:它允許你通過擴展子類中的部分內容來改變部分算法。策略基於組合機制:你可以通過對相應行為提供不同的策略來改變對象的部分行為。模板方法在類層次上運作,因此它是靜態的。策略在對象層次上運作,因此允許在運行時切換行為。

狀態可被視為策略的擴展。兩者都基於組合機制:它們都通過將部分工作委派給“幫手”對象來改變其在不同情景下的行為。策略使得這些對象相互之間完全獨立,它們不知道其他對象的存在。但狀態模式沒有限制具體狀態之間的依賴,且允許它們自行改變在不同情景下的狀態。

以上就是java設計模式策略模式圖文示例詳解的詳細內容,更多關於java設計模式策略模式的資料請關註WalkonNet其它相關文章!

推薦閱讀: