從架構層面看設計模式

大部分講解設計模式的書或者文章,都是從代碼層面來說解設計模式,看的時候都懂,可是到真正用的時候,仍是理不清、想不明。算法

本文嘗試從架構層面來聊一聊設計模式。經過將使用設計模式的代碼和不使用設計模式的代碼分別放到架構中,來看看設計模式對架構所產生的影響。設計模式

通常模式講解套路

通常講解設計模式的套路是:markdown

  • 說明模式的意圖
  • 說明模式的適用場景
  • 給出模式的類結構
  • 給出對應的代碼示例

以策略模式爲例:數據結構

意圖:定義一系列的算法,把它們一個個封裝起來, 而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。架構

適用性ide

  • 許多相關的類僅僅是行爲有異。「策略」提供了一種用多個行爲中的一個行爲來配置一個類的方法。
  • 須要使用一個算法的不一樣變體。例如,你可能會定義一些反映不一樣的空間/時間權衡的算法。當這些變體實現爲一個算法的類層次時,可使用策略模式。
  • 算法使用客戶不該該知道的數據。可以使用策略模式以免暴露覆雜的、與算法相關的數據結構。
  • 一個類定義了多種行爲, 而且這些行爲在這個類的操做中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。

類結構函數

從架構層面看設計模式

示例代碼oop

public class Context {

 //持有一個具體策略的對象
 private Strategy strategy;

 /**
 * 構造函數,傳入一個具體策略對象
 * @param strategy 具體策略對象
 */
 public Context(Strategy strategy){
 this.strategy = strategy;
 }
 
 /**
 * 策略方法
 */
 public void invoke(){
 strategy.doInvoke();
 }
}

public interface Strategy {

 /**
 * 策略方法
 */
 public void doInvoke();
}

public class StrategyA implements Strategy {

 @Override
 public void doInvoke() {
 System.out.println("InvokeA");
 }
}

public class StrategyB implements Strategy {
 @Override
 public void doInvoke() {
 System.out.println("InvokeB");
 }
}
複製代碼

從上面的講解,你能理解策略模式嗎?你是否有以下的一些疑問?this

  • 使用策略模式和我直接寫if-else具體的優點在哪裏?
  • if-else不是挺簡單的,爲何要多寫這麼多的類?
  • 如何將Strategy給設置到Context中?
  • 我該如何判斷將哪一個實現設置給Context?仍是ifelse?!那拆成這麼多的類不是脫褲子放屁嗎?

將模式放入架構中

產生這些疑問的緣由,是咱們在孤立的看設計模式,而沒有把設計模式放到實際的場景中。編碼

當咱們將其放到實際項目中時,咱們實際是須要一個客戶端來組裝和調用這個設計模式的,以下圖所示:

從架構層面看設計模式

public class Client {
 
 public static void main(String[] args) {
 Strategy strategy;
 if("A".equals(args[0])) {
 strategy = new StrategyA();
 } else {
 strategy = new StrategyB();
 }
 Context context = new Context(strategy);
 context.invoke();
 } 
}
複製代碼

做爲比較,這裏也給出直接使用ifelse時的結構和代碼:

從架構層面看設計模式

public class Client {
 public static void main(String[] args) {
 Context context = new Context(args[0]);
 context.invoke();
 }
}

public class Context {
 public void invoke(String type) {
 if("A".equals(type)) {
 System.out.println("InvokeA");
 } else if("B".equals(type)) {
 System.out.println("InvokeB");
 }
 }
}
複製代碼

乍看之下,使用ifelse更加的簡單明瞭,不過別急,下面咱們來對比一下兩種實現方式的區別,來具體看看設計模式所帶來的優點。

邊界不一樣

首先,使用策略模式使得架構的邊界與使用ifelse編碼方式的架構的邊界不一樣。策略模式將代碼分紅了三部分,這裏稱爲:

  • 調用層:將下層的業務邏輯組裝起來,造成完整的可執行流程
  • 邏輯層:具體的業務邏輯流程
  • 實現層:實現業務邏輯中可替換邏輯的具體實現

從架構層面看設計模式

而ifelse將代碼分紅了兩部分:

  • 調用層:將下層的業務邏輯組裝起來,造成完整的可執行流程
  • 邏輯層:具體的業務邏輯流程及具體邏輯

從架構層面看設計模式

解耦

在ifelse實現中,「邏輯流程」和「邏輯實現」是硬編碼在一塊兒的,明顯的緊耦合。而策略模式將「邏輯流程」和「邏輯實現」拆分開,對其進行了解耦。

解耦後,「邏輯流程」和「邏輯實現」就能夠獨立的進化,而不會相互影響。

獨立進化

假設如今要調整業務流程。對於策略模式來講,須要修改的是「邏輯層」;而對於ifelse來講,須要修改的也是「邏輯層」。

假設如今要新增一個策略。對於策略模式來講,須要修改的是「實現層」;而對於ifelse來講,須要修改的仍是「邏輯層」。

在軟件開發中,有一個原則叫單一職責原則,它不只僅是針對類或方法的,它也適用於包、模塊甚至子系統。

對應到這裏,你會發現,ifelse的實現方式違背了單一職責原則。使用ifelse實現,使得邏輯層的職責不單一了。當業務流程須要調整時,須要調整邏輯層的代碼;當具體的業務邏輯實現須要調整時,也須要調整邏輯層。

而策略模式將業務流程和具體的業務邏輯拆分到了不一樣的層內,使得每一層的職責相對的單一,也就能夠獨立的進化。

對象彙集

咱們從新來觀察一下策略模式的架構圖,再對照上面的調用代碼,你有沒有發現缺乏了點什麼?

在Client中,咱們要根據參數斷定來實例化了StategyA或StategyB對象。也就是說,「調用層」使用了「實現層」的代碼,實際調用邏輯應該是這樣的:

從架構層面看設計模式

能夠看到,Client與StategyA和StategyB是強依賴的。這會致使兩個問題:

  • 對象分散:若是StategyA或StategyB的實例化方法須要調整,全部實例化代碼都須要進行調整。或者若是新增了StategyC,那麼全部將Stategy設置到Context的相關代碼都須要調整。
  • 穩定層依賴不穩定層:通常狀況下,「實現層」的變更頻率較高;而對於「調用層」來講,調用流程肯定後,基本就不會變化了。讓一個基本不變的層去強依賴一個頻繁變化的層,顯然是有問題的。

咱們先來解決「對象分散」的問題,下一節來解決「穩定層依賴不穩定層」的問題!

對於「對象分散」的問題來講,建立型的設計模式基本能解決這個問題,對應到這裏,能夠直接使用工廠方法!

從架構層面看設計模式

使用了工廠方法後,構建代碼被限制在了工廠方法內部,當策略對象的構造邏輯調整時,咱們只須要調整對應的工廠方法就能夠了。

依賴倒置

如今「調用層」只和「實現層」的StategyFactoryImpl有直接的關係,解決了「對象分散」的問題。可是即便只依賴一個類,調用層依然和實現層是強依賴關係。

該如何解決這個問題呢?咱們須要依賴倒置。通常方法是使用接口,例如這裏的「邏輯層」和「實現層」就是經過接口來實現了依賴倒置:「邏輯層」並不強依賴於「實現層」的任何一個類。箭頭方向都是從「實現層」指向「邏輯層」的,因此稱爲依賴倒置

從架構層面看設計模式

可是對於「調用層」來講,此方法並不適用,由於它須要實例化具體的對象。那咱們該如何處理呢?

相信你已經想到了,就是咱們一直在用的IOC!經過注入的方式,使得依賴倒置!咱們能夠直接替換掉工廠方法。

從架構層面看設計模式

能夠看到,經過依賴注入,使得「調用層」和「實現層」都依賴於「邏輯層」。因爲「邏輯層」也是相對較穩定的,因此「調用層」也就不會頻繁的變化,如今須要變化的只有「實現層」了。

邏輯顯化

最後一個區別就是設計模式使得邏輯顯化。什麼意思呢?

當你使用ifelse的時候,實際上你須要深刻到具體的ifelse代碼,你才能知道它的具體邏輯是什麼。

對於使用設計模式的代碼來講,咱們回過頭來看上面的架構圖,從這張圖你就能看出來對應的邏輯了:

  • 由StrategyFactory實例化全部Strategy的實現
  • Client經過StrategyFactory獲取Strategy實例,並將其設置到Context中
  • 由Context委託給具體的Strategy來執行具體的邏輯

至於具體的Strategy邏輯是什麼樣子的,你能夠經過類名或方法名來將其顯化出來!

總結

本文經過將使用設計模式的代碼和不使用設計模式的代碼分別放到架構中,對比設計模式對架構所產生的影響:

  • 劃分邊界
  • 解耦
  • 獨立進化
  • 對象彙集
  • 依賴倒置
  • 邏輯顯化

參考資料

  • 《架構整潔之道》
  • 《深刻淺入設計模式》

原文連接:mp.weixin.qq.com/s/8eKNo6\_t…

相關文章
相關標籤/搜索