小課堂week18 編程範式巡禮第三季 談談依賴反轉

編程範式巡禮第三季--談談依賴反轉

今天會進入深一點的主題,談一個軟件開發的"道":依賴反轉。根據個人觀察,這也是架構師與程序員的分水嶺之一。java

什麼是依賴反轉

引出問題

讓咱們從Uncle Bob和小明的一段對話開始。原文地址:如何成爲一名優秀的架構師程序員

小明:我要領導一個團隊,還要作全部關於數據庫、框架和Web服務器的重要決定。
Uncle Bob:好吧,若是是這樣,你就不必成爲一名軟件架構師了。
小明:固然有必要了!我要成爲一個可以作全部重要決定的人。
Uncle Bob:這樣很好,只是你沒有列出哪些纔是重要的決定。你剛纔說的那些跟重要的決定沒有什麼關係。算法

Bob大叔一上來就拋出了一個反常識的觀點,工具的選擇決策並非重要的問題,爲何這麼認爲?spring

Uncle Bob:你認爲業務邏輯依賴數據庫,但實際上不是這樣的。若是你的架構足夠好,最起碼業務邏輯不該該依賴數據庫。
小明:若是業務邏輯對數據庫一無所知,它怎麼使用這些工具呢?
Uncle Bob:依賴反轉。你要讓數據庫依賴業務邏輯,而不是讓業務邏輯依賴數據庫。數據庫

Bob大叔認爲重要的是業務邏輯的實現,工具只是服務於業務邏輯。但小明提出的是一個很是實際的問題,業務邏輯是基於工具來實現的,就比如畫家是依賴畫具創做的,東方和西方因爲畫具不一樣,展示的藝術做品就徹底不一樣。這個時候Bob大叔拋出了此次的重頭戲:依賴反轉。編程

小明:那就更加費解了!既然上層策略(假設你指的是業務邏輯)要調用下層策略(假設你指的是數據庫),那麼就應該是上層策略依賴下層策略,就像調用者依賴被調用者同樣。這是衆所周知的!
Uncle Bob:在運行時確實是這樣的,但在編譯時咱們要把依賴反轉過來。上層策略的代碼不要引用任何下層策略的代碼。json

小明的說法有道理,適用於咱們現實生活常識,可是計算機領域偏偏是有本身獨特規則的,Bob大叔指出了這一點。咱們編寫的計算機代碼並非直接運行的,當中會通過編譯器的處理,而這個中間處理,讓依賴反轉變成了可能。通俗點說,在計算機世界中,工具是能夠晚於業務邏輯出現的。安全

代碼實例

下面來看代碼的例子:服務器

小明:在Java裏,發送者最起碼要知道接收者的基本類型。
Uncle Bob:是的。不過,不論是哪種狀況,發送者都不知道接收者具體的類型。數據結構

發送者(業務邏輯):BusinessRule
基本類型:BusinessRuleGateway
具體類型(工具):MySqlBusinessRuleGateway

package businessRules;
import entities.Something;
public class BusinessRule {  
    private BusinessRuleGateway gateway;  
    public BusinessRule(BusinessRuleGateway gateway) {    
        this.gateway = gateway;  }  
    public void execute(String id) {    
        gateway.startTransaction();    
        Something thing = gateway.getSomething(id);    
        thing.makeChanges();    
        gateway.saveSomething(thing);    
        gateway.endTransaction();  
    }
}
import entities.Something;
public interface BusinessRuleGateway {  
    Something getSomething(String id);  
    void startTransaction();  
    void saveSomething(Something thing);  
    void endTransaction();
}
package database;
import businessRules.BusinessRuleGateway;
import entities.Something;
public class MySqlBusinessRuleGateway implements BusinessRuleGateway {  
    public Something getSomething(String id) {    
        // 從MySQL裏讀取一些數據  
    }  
    public void startTransaction() {    
        // 開始一個事務  
    }  
    public void saveSomething(Something thing) {    
        // 把數據保存到MySQL  
    }  
    public void endTransaction() {    
        // 結束事務  
    }
}

能夠看到,業務邏輯BusinessRule是在運行時對工具MySqlBusinessRuleGateway進行調用的,但在編譯時,二者並無依賴關係。

基本類型的問題

一切都是那麼的完美,依賴確實反轉了,可是存在一個問題,就是多出了一個東西:基本類型BusinessRuleGateway。

小明:這樣的話,若是業務邏輯須要全部的工具,那麼你必須把全部工具都放到Gateway接口裏。
小明:這樣的話,你就會有不少接口,並且有不少實現類。
小明:這樣子很浪費時間!我爲何要這樣作呢?這樣只會增長更多的代碼。
Uncle Bob:這個叫做接口分離原則。每一個業務邏輯只使用一部分數據庫工具,因此每一個業務邏輯只定義可以知足須要的接口。

小明提出了一個開發中很實際的問題,基本類型是多餘的代碼,會增長工做。Bob大叔則以爲,基本類型能夠認爲是對工具的需求,也是須要思考的部分。

小明:但首先要先決定使用什麼數據庫、Web服務器或框架啊!
Uncle Bob:不,實際上應該在開發後期纔開始作這些事情——在你掌握了更多信息以後。
哀哉,當架構師草率地決定要使用一個數據庫,後來卻發現使用文件系統效率更高。
哀哉,當架構師草率地決定使用一個Web服務器,後來卻發現團隊須要的不過是一個Socket接口。
哀哉,當架構師草率地決定使用一個框架,後來卻發現框架提供的功能是團隊不須要的,反而給團隊帶來了諸多約束。
幸哉,當架構師在掌握了足夠多的信息後才決定該用什麼數據庫、Web服務器或框架。幸哉,當架構師爲團隊鑑別出運行緩慢、耗費資源的IO設備和框架,這樣他們就能夠構建飛速運行的輕量級測試環境。
幸哉,當架構師把注意力放在那些真正重要的事情上,並把那些不重要的事情放在一邊。

Bob大叔用一段詠歎調結束了這一次對話,提出了他的核心見解:架構設計要能適應將來的變化。

  1. 在技術層面,最大的挑戰來自於沒法預測的性能容量增加,須要不斷與更先進的工具進行接軌。
  2. 在業務層面,響應要求日益嚴峻,代碼的修改爲本(主要由耦合帶來)會成爲重要的生產力指標。
    解決思路是模塊與工具解耦、模塊與模塊解耦,依賴反轉無疑是實現解耦有力方法,是架構師的有力工具。

依賴反轉思想的擴展

依賴反轉不只僅是一個模式或者方法,更重要的是其體現的解耦思想,下面再介紹兩個具備一樣思想的重要範式。

切面範式

切面Aspect是與程序的縱向主流執行方向橫向正交的關注焦點。此類代碼以片段的形式散落在各處,雖具備類似的邏輯,卻沒法用傳統的方法提煉成模塊。典型的例子如:日誌輸出、代碼跟蹤、性能監控、異常處理、安全檢查、事務處理等。爲解決此類問題,AOP應運而生。它將每類橫切關注點封裝到單獨的Aspect模塊中,經過定義執行點和代碼綁定起來。

AOP從描述來看是比較抽象的,簡單來講就是除了上面提到的模塊和工具、模塊和模塊之外,在模塊內代碼片段之間也存在必定的依賴關係,而AOP是對代碼片段解耦的方法。
下面是AOP代碼片段(進行日誌跟蹤)。

@Around("execution(* spring.services.MyDemoService3.*(..))")
    public void traceBusiness(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("before enter method "+jp.getSignature().getName());
        jp.proceed(jp.getArgs());
        System.out.println("after enter method "+jp.getSignature().getName());
    }

有人會問了,爲啥沒有業務邏輯。這是由於,AOP與業務邏輯是能夠無關的,業務邏輯角度有可能沒法感知。可是信息不透明容易造成惡性的發展,因此實際使用時要運用annotation進行限制,避免過分使用。

泛型範式

泛型編程是算法導向的,即以算法爲起點和中心點,逐漸將其所涉及的概念內涵模糊化、外延擴大化,將其所涉及的運算抽象化、通常化,從而擴展算法的適用範圍。

泛型又是解耦思想在具體算法實現中的運用。算法在運行時是包含數據結構和算法邏輯兩個部分的,這個時候咱們能夠用基本類型來替代具體的數據結構,實現二者的解耦。

下面是代碼例子(將一組Json字符轉換爲對象),轉換的目標對象與算法邏輯是無關的,因此用基本類型T來進行了替代。

static <T> List<T> convertJsonToPojo(List<String> jsonStrings, Class<T> c, boolean generateSeq) {
        List<T> result = new ArrayList<>();
        ObjectMapper objectMapper = ObjectMapperFactory.create();
        jsonStrings.forEach(map -> {
                result.add(convertJsonToPojo(map, c, generateSeq, objectMapper));
        });
        return result;
    }

泛型範式與平常工做很是接近,咱們每時每刻都能接觸到,是除了基本範式以外最爲古老的了。編寫泛型代碼必定會用到抽象思惟,這不是一種常識思惟,但能夠做爲一種練習,是由低到高的修煉捷徑。

小結

最後想說的是,最重要的不是依賴反轉這個方法,而是依賴反轉的思想。練成了這種思想,小處講,能夠節省代碼、提升效率。大處講,能夠適應變化、應對將來。這裏只是拋個磚,請有志於架構設計的同窗務必深刻掌握。

相關文章
相關標籤/搜索