怎樣理解依賴反轉

依賴反轉的定義

在面向對象開發中有一個基本的設計原則叫依賴反轉/倒置(dependency inversion)。維基百科對于它的定義如下:

  1. 高層次的模塊不應該依賴于低層次的模塊,兩者都應該依賴于抽象接口。
  2. 抽象接口不應該依賴于具體實現。而具體實現則應該依賴于抽象接口。

剖析

但是當我看到這樣的定義時,我是十分困惑的。什么是依賴?什么是反轉?為什么定義了抽象接口,作為中間層會被稱為依賴反轉?如果你和我有相同的疑問,不妨跟我一起看下去。

我們先看一下下面這段傳統的示例代碼片段 1:

class B {
  void mb(){}
}

class A {
  public void cb() {
      (new B()).mb();
  }
}

上面的代碼非常簡單,在 Acb 方法創建了 B 的對象并調用了它的 mb 方法。 在這里 A 需要 B 才能實現 cb 的功能,所以我們稱 A 依賴于 B。 這里我們同時需要知道的是 A 是 “高層次”, “B” 屬于低層次。

按照‘‘依賴反轉’’的定義, 高層次的模塊不應該依賴于低層次的模塊,兩者都應該依賴于抽象接口, 所以 “B” 是要抽象出來的。我們把上面的代碼改造成下面的代碼片段 2:

interface IB {
  void mb();
}

class B implements IB {
  void mb(){}
}

class A {
  public void cb(IB b) {
      b.mb();
  }
}

這樣A 不再依賴于具體的 B 的對象,而是依賴于 IB 接口。B 也實現了 IB 接口。可以說這里我們已經就滿足了 “依賴反轉” 的定義:

  1. A 不依賴于 BAB 都依賴于抽象接口 IB
  2. IB 不依賴于具體實現 AB。而具體實現 B、A 則應該依賴于抽象接口 IB。

總結下,現在依賴關系的變化:A -> B 變成了 A -> IB, B -> IB

問題來了,“反轉” 從何而來?不妨繼續看看下面的代碼。

void main() {
  (new A()).cb();
}

在這段代碼中,我們在 main 函數,調用了 代碼片段 1A.cb 方法。不難理解,在這里,main 函數其實又是 A 的高層次。我們實現的是高層次依次調用低層次的具體實現, main -> new A() -> new B()。接著我們看看 main 調用 代碼片段2 會有什么不同?

void main() {
  IB b = new B();
  (new A(b)).cb();
}

寫到這里,我忽然覺得不需要解釋什么了,代碼已經說明了一切。對于高層次的 A 來講,低層次的 B 卻被先創建了出來,變成了 main -> new B() -> new A()。這就是 “反轉” 的意思。低層次的實現被推遲到了更高的層次。

總結,依賴反轉是從調用者的視角看的。傳統的代碼實現,是高層次調用低層次的具體實現,但是使用了依賴反轉后,低層次的具體實現,被提升到了更高的層次。

擴展

依賴反轉的優點:

上面我們結合概念和代碼,解釋了什么是依賴反轉。那么依賴反轉的優點是什么?

  1. 解耦合:A 不再強綁定于 B, 任何實現了 IB 接口的類,都能被傳入 A 中;
  2. 擴展性:任何實現了 IB 接口的類,都能被傳入到 A 中,而無需修改 A 的代碼;

其他 “依賴” 的表現形式

class A  {
   C mc;
   public A(C c) {
    mc = c; 
  }
  public setD(D d) {
    ....
  }
}

在上面的構造函數, setD 都屬于依賴。想必會加深對依賴的理解。

依賴反轉和依賴注入是什么關系

依賴注入是依賴反轉的一種具體的實現方式。

自問自答

自問: 如果沒有創建 IB 接口,通過下面的方式實現,是不是也是依賴反轉?

class A {
  public void cb(B b) {
      b.mb();
  }
}

void main() {
    B b = new B();
    (new A(b)).cb();
}

自答: 首先毫無疑問這是依賴注入,依賴注入是依賴反轉的一種具體實現,所以是。在 dart 語言中,類本身也是接口,從這種層面上來講,完全是 ok 的。但是從 java 來講又不完全是,因為不符合依賴反轉的具體定義,只能傳 B 的對象, 這就破壞了代碼的靈活性、擴展性、非耦合性,就算它是,也是一種很爛的實現。

所以我們應該嚴格按照依賴反轉的定義去編寫我們具體的代碼。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容