輕鬆學,淺析依賴倒置(DIP)、控制反轉(IOC)和依賴注入(DI) 依賴注入和控制反轉的理解,寫的太好了。

輕鬆學,淺析依賴倒置(DIP)、控制反轉(IOC)和依賴注入(DI)

版權聲明:本文爲博主原創文章,未經博主容許不得轉載。 https://blog.csdn.net/briblue/article/details/75093382

寫這篇文章的緣由是這兩天在編寫關於 Dagger2 主題的博文時,花了大量的精力來解釋依賴注入這個概念。後來想一下,這些在面向對象開發過程當中與依賴相關的諸多術語和概念實際狀況下很是的抽象,所以獨立成文也有就必定的意義,旨在幫助初學者快速地對這些概念有一個大體的印象,而後經過一些實例來領悟它們之中的內在奧祕。html

什麼是依賴(Dependency)?

依賴是一種關係,通俗來說就是一種須要。 
這裏寫圖片描述java

程序員須要電腦,由於沒有電腦程序員就沒有辦法編寫代碼,因此說程序員依賴電腦,電腦被程序員依賴。程序員

在面向對象編程中,代碼能夠這樣編寫。spring

class Coder {

    Computer mComputer;

    public Coder () { mComputer = new Computer(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Coder 的內部持有 Computer 的引用,這就是依賴在編程世界中的體現。數據庫

依賴倒置 (Dependency inversion principle)

依賴倒置是面向對象設計領域的一種軟件設計原則。 
軟件設計有 6 大設計原則,合稱 SOLID。編程

有人會有疑惑,設計原則有什麼用呢?設計模式

設計原則是前輩們總結出來的經驗,你能夠把它們看做是內功心法。markdown

只要你在日常開發中按照設計原則進行編碼,假以時日,你編程的功力將會大增。架構

依賴倒置原則的定義以下:框架

  1. 上層模塊不該該依賴底層模塊,它們都應該依賴於抽象。
  2. 抽象不該該依賴於細節,細節應該依賴於抽象。

乍一看,這會讓初學者摸不清頭腦。這種學術性的歸納語言近乎於軟件行業中的哲學。可實質上,它確實稱得上是哲學,如今 SOLID 幾乎等同於面向對象開發中的金科玉律,可是也正由於它的高度歸納、它的晦澀難懂,對於廣大初學者而言這是一件很是不友好的事物。

咱們該怎麼理解上面的定義呢?咱們須要咬文嚼字,各個突破。

什麼是上層模塊和底層模塊?

無論你認可不認可,「有人的地方就有江湖」,咱們都說人人平等,可是對於任何一個組織機構而言,它必定有架構的設計有職能的劃分。按照職能的重要性,天然而然就有了上下之分。而且,隨着模塊的粒度劃分不一樣這種上層與底層模塊會進行變更,也許某一模塊相對於另一模塊它是底層,可是相對於其餘模塊它又多是上層。

這裏寫圖片描述

公司管理層就是上層,CEO 是整個事業羣的上層,那麼 CEO 職能之下就是底層。

而後,咱們再以事業羣爲整個體系劃分模塊,各個部門經理以上部分是上層,那麼之下的組織均可以稱爲底層。

由此,咱們能夠看到,在一個特定體系中,上層模塊與底層模塊能夠按照決策能力高低爲準繩進行劃分。

那麼,映射到咱們軟件實際開發中,通常咱們也會將軟件進行模塊劃分,好比業務層、邏輯層和數據層。 
這裏寫圖片描述

業務層中是軟件真正要進行的操做,也就是作什麼。 
邏輯層是軟件現階段爲了業務層的需求提供的實現細節,也就是怎麼作。 
數據層指業務層和邏輯層所須要的數據模型。

所以,如前面所總結,按照決策能力的高低進行模塊劃分。業務層天然就處於上層模塊,邏輯層和數據層天然就歸類爲底層。 
這裏寫圖片描述

什麼是抽象和細節?

抽象如其名字同樣,是一件很抽象的事物。抽象每每是相對於具體而言的,具體也能夠被稱爲細節,固然也被稱爲具象。

好比: 
1. 這是一幅畫。畫是抽象,而油畫、素描、國畫而言就是具體。 
2. 這是一件藝術品,藝術品是抽象,而畫、照片、瓷器等等就是具體了。 
3. 交通工具是抽象,而公交車、單車、火車等就是具體了。 
4. 表演是抽象,而唱歌、跳舞、小品等就是具體。

上面能夠知道,抽象能夠是物也能夠是行爲。

具體映射到軟件開發中,抽象能夠是接口或者抽象類形式。

public interface Driveable { void drive(); } class Bike implements Driveable{ @Override public void drive() { // TODO Auto-generated method stub System.out.println("Bike drive."); } } class Car implements Driveable{ @Override public void drive() { // TODO Auto-generated method stub System.out.println("Car drive."); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Driveable 是接口,因此它是抽象,而 Bike 和 Car 實現了接口,它們被稱爲具體。

如今,咱們理解了依賴、上層模塊、底層模塊、抽象和具體。這樣咱們能夠正式開始學習依賴倒置原理這個概念了?

依賴倒置的好處

在日常的開發中,咱們大概都會這樣編碼。

public class Person { private Bike mBike; public Person() { mBike = new Bike(); } public void chumen() { System.out.println("出門了"); mBike.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

咱們建立了一個 Person 類,它擁有一臺自行車,出門的時候就騎自行車。

public class Test1 { public static void main(String[] args) { // TODO Auto-generated method stub Person person = new Person(); person.chumen(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

執行結果以下:

出門了
Bike drive.
  • 1
  • 2
  • 3

不過,自行車適應很短的距離。若是,我要出門逛街呢?自行車就不大合適了。因而就要改爲汽車。

public class Person { private Bike mBike; private Car mCar; public Person() { //mBike = new Bike(); mCar = new Car(); } public void chumen() { System.out.println("出門了"); //mBike.drive(); mCar.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

咱們須要修改 Person 這個類的代碼。

不過,若是我要到北京去,那麼汽車也不合適了。

class Train implements Driveable{
    @Override public void drive() { // TODO Auto-generated method stub System.out.println("Train drive."); } } package com.frank.test; public class Person { private Bike mBike; private Car mCar; private Train mTrain; public Person() { //mBike = new Bike(); //mCar = new Car(); mTrain = new Train(); } public void chumen() { System.out.println("出門了"); //mBike.drive(); //mCar.drive(); mTrain.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

咱們添加了 Train 這個最新的實現類,而後再次修改了 Person 這個類。

有沒有一種方法能讓 Person 的變更少一點呢?由於這是最基礎的演示代碼,若是工程大了,代碼複雜了,Person 面對需求變更時改動的地方會更多。

而依賴倒置原則正好適用於解決這類狀況。

下面,咱們嘗試運用依賴倒置原則對代碼進行改造。

咱們再次回顧下它的定義。

  1. 上層模塊不該該依賴底層模塊,它們都應該依賴於抽象。
  2. 抽象不該該依賴於細節,細節應該依賴於抽象。

首先是上層模塊和底層模塊的拆分。

按照決策能力高低或者重要性劃分,Person 屬於上層模塊,Bike、Car 和 Train 屬於底層模塊。

上層模塊不該該依賴於底層模塊。 
可是

public class Person { private Bike mBike; private Car mCar; private Train mTrain; public Person() { //mBike = new Bike(); //mCar = new Car(); mTrain = new Train(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Person 這個類顯然是依賴於 Bike 和 Car。Person 類中 chumen() 的能力徹底依賴於屬性 Bike 或者 Car 對象,也就是說 Person 把本身的能力依賴在 Bike 和 Car 身上。

上層和底層都應該依賴於抽象。

這裏寫圖片描述

咱們的代碼中,Person 沒有依賴抽象,因此咱們得引進抽象。

而底層的抽象是什麼,是 Driveable 這個接口。

public class Person { // private Bike mBike; // private Car mCar; // private Train mTrain; private Driveable mDriveable; public Person() { //mBike = new Bike(); //mCar = new Car(); //mTrain = new Train(); mDriveable = new Train(); } public void chumen() { System.out.println("出門了"); //mBike.drive(); //mCar.drive(); //mTrain.drive(); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

執行結果以下:

出門了
Train drive.
  • 1
  • 2

如今,Person 類中 chumen() 這個方法依賴於 Driveable 接口的抽象,它沒有限定本身出行的可能性,任何 Car、Bike 或者是 Train 均可以的。

到這一步,咱們能夠說是符合了上層不依賴於底層,依賴於抽象的準則了。

那麼,抽象不該該依賴於細節,細節應該依賴於抽象又是什麼意思呢?

以上面爲例,Driveable 是抽象,它表明一種行爲,而 Bike、Car、Train 都是實現細節。

Person 須要的是 Driveable,須要的是交通工具,但不是說交通工具必定是 Bike、Car、Train。將來也多是 AirPlane。

class AirPlane implements Driveable{
    @Override public void drive() { // TODO Auto-generated method stub System.out.println("AirPlane fly."); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那麼一個 Person,它下次出門改爲飛機能夠嗎?固然能夠的。由於依賴倒置的原因,Person 展示出了極度的可擴展性。

這裏寫圖片描述

上面的內容就是依賴倒置原則。

有人會考慮到倒置這個詞,我的的理解是倒置是改變的意思。

原本正常編碼下,確定會出現上層依賴底層的狀況,而依賴倒置原則的應用則改變了它們之間依賴的關係,它引進了抽象。上層依賴於抽象,底層的實現細節也依賴於抽象,因此依賴倒置咱們能夠理解爲依賴關係被改變,若是很是糾結於倒置這個詞,那麼倒置的實際上是底層細節,本來它是被上層依賴,如今它倒要依賴與抽象的接口。

這裏寫圖片描述

能夠看到,依賴倒置實質上是面向接口編程的體現。

控制反轉 (IoC)

控制反轉 IoC 是 Inversion of Control的縮寫,意思就是對於控制權的反轉,對麼控制權是什麼控制權呢? 
你們從新審視上面的代碼。

public class Person { // private Bike mBike; // private Car mCar; // private Train mTrain; private Driveable mDriveable; public Person() { //mBike = new Bike(); //mCar = new Car(); //mTrain = new Train(); mDriveable = new Train(); } public void chumen() { System.out.println("出門了"); //mBike.drive(); //mCar.drive(); //mTrain.drive(); mDriveable.drive(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

雖然,chumen() 這個方法再也不由於出行方式的改變而變更,可是每次更改出行方式的時候,Person 這個類仍是要修改。

Person 類仍是要實例化 mDriveable 的接口對象。

public Person() { //mBike = new Bike(); //mCar = new Car(); //mTrain = new Train(); mDriveable = new Train(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Person 本身掌控着內部 mDriveable 的實例化。 
如今,咱們能夠更改一種方式。將 mDriveable 的實例化移到 Person 外面。

public class Person { private Driveable mDriveable; public Person(Driveable driveable) { this.mDriveable = driveable; } public void chumen() { System.out.println("出門了"); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

就這樣不管出行方式怎麼變化,Person 這個類都不須要更改代碼了。

public class Test1 { public static void main(String[] args) { // TODO Auto-generated method stub Bike bike = new Bike(); Car car = new Car(); Train train = new Train(); // Person person = new Person(bike); // Person person = new Person(car); Person person = new Person(train); person.chumen(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在上面代碼中,Person 把內部依賴的建立權力移交給了 Test1 這個類中的 main() 方法。也就是說 Person 只關心依賴提供的功能,但並不關心依賴的建立。

這種思想其實就是 IoC,IoC 是一種新的設計模式,它對上層模塊與底層模塊進行了更進一步的解耦。控制反轉的意思是反轉了上層模塊對於底層模塊的依賴控制。 
好比上面代碼,Person 再也不親自建立 Driveable 對象,它將依賴的實例化的權力交接給了 Test1。而 Test1 在 IoC 中又指代了 IoC 容器 這個概念。

再舉一個例子,咱們到餐廳去叫外賣,餐廳有專門送外賣的外賣員,他們的使命就是及時送達外賣食品。

依照依賴倒置原則,咱們能夠建立這樣一個類。

public abstract class WaimaiYuan { protected Food food; public WaimaiYuan(Food food) { this.food = food; } abstract void songWaiMai(); } public class Xiaohuozi extends WaimaiYuan { public Xiaohuozi(Food food) { super(food); } @Override void songWaiMai() { System.out.println("我是小夥子,爲您送的外賣是:"+food); } } public class XiaoGuniang extends WaimaiYuan { public XiaoGuniang(Food food) { super(food); } @Override void songWaiMai() { System.out.println("我是小姑娘,爲您送的外賣是:"+food); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

WaimaiYuan 是抽象類,表明送外賣的,Xiaohuozi 和 XiaoGuniang 是它的繼承者,說明他們均可以送外賣。WaimaiYuan 都依賴於 Food,可是它沒有實例化 Food 的權力。

再編寫食物類代碼

public abstract class Food { protected String name; @Override public String toString() { return name; } } public class PijiuYa extends Food { public PijiuYa() { name = "啤酒鴨"; } } public class DuojiaoYutou extends Food { public DuojiaoYutou() { name = "剁椒魚頭"; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

Food 是抽象類,PijiuYa 和 DuojiaoYutou 都是實現細節。

IoC 少不了 IoC 容器,也就是實例化抽象的地方。咱們編寫一個餐廳類。

public class Restaurant { public static void peican(int orderid,int flowid) { WaimaiYuan person; Food food; if ( orderid == 0) { food = new PijiuYa(); } else { food = new DuojiaoYutou(); } if ( flowid % 2 == 0 ) { person = new Xiaohuozi(food); } else { person = new XiaoGuniang(food); } person.songWaiMai(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

orderid 表明菜品編號,0 是啤酒鴨,其它則是剁椒魚頭。 
flowid 是訂單的流水號碼。 餐廳根據流水編碼的不一樣來指派小夥子或者小姑娘來送外賣,編寫測試代碼。

public class IocTest { public static void main(String[] args) { Restaurant.peican(0, 0); Restaurant.peican(0, 1); Restaurant.peican(1, 2); Restaurant.peican(0, 3); Restaurant.peican(1, 4); Restaurant.peican(1, 5); Restaurant.peican(1, 6); Restaurant.peican(0, 7); Restaurant.peican(0, 8); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

餐廳一次性送了 9 份外賣。

我是小夥子,爲您送的外賣是:啤酒鴨
我是小姑娘,爲您送的外賣是:啤酒鴨
我是小夥子,爲您送的外賣是:剁椒魚頭
我是小姑娘,爲您送的外賣是:啤酒鴨
我是小夥子,爲您送的外賣是:剁椒魚頭
我是小姑娘,爲您送的外賣是:剁椒魚頭
我是小夥子,爲您送的外賣是:剁椒魚頭
我是小姑娘,爲您送的外賣是:啤酒鴨
我是小夥子,爲您送的外賣是:啤酒鴨
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

能夠看到的是,由於有 Restaurant 這個 IoC 容器存在,大大地解放了外賣員的生產力,外賣員再也不依賴具體的食物,具體的食物也再也不依賴於特定的外賣員。也就是說,只要是食物外賣員就能夠送,任何一種食物能夠被任何一位外賣員送。

你們細細體會這是怎麼樣一種靈活性。若是非要外賣員本身決定配送什麼食物,人少則還行,人多的時候,訂單多的時候確定會亂成一鍋粥。

因此,實際工做當中,基本上都是按照專業的人幹專業的事這種基本規律運行。外賣員沒有能力也沒有義務去親自決定該送什麼訂單,這種權力在於餐廳,只要餐廳配置好就 OK 了。

記住 配置 這個詞。

在軟件開發領域,相似餐廳這種調度配置而後決定依賴關係的 IOC 容器有許多框架好比 Spring。可是,因爲我自己是 Android 開發的,對於 Spring 知之甚少,因此對這一塊不作過多介紹。

但做爲 IoC 容器,無非是針對配置而後動態生成依賴關係。有的配置是開發者按照規則編寫在 xml 格式文件中,有些配置則是利用 Java 中的反射與註解。

IoC 模式最核心的地方就是在於依賴方與被依賴方之間,也就是上文中說的上層模塊與底層模塊之間引入了第三方,這個第三方統稱爲 IoC 容器,由於 IoC 容器的介入,致使上層模塊對於它的依賴的實例化控制權發生變化,也就是所謂的控制反轉的意思。

總之,由於 IoC 容器的存在,使得開發者編寫大型系統工程的時候極大地解放了生產力。

依賴注入(Dependency injection)

依賴注入,也常常被簡稱爲 DI,其實在上一節中,咱們已經見到了它的身影。它是一種實現 IoC 的手段。什麼意思呢?

public class Person { private Driveable mDriveable; public Person() { mDriveable = new Train(); } public void chumen() { System.out.println("出門了"); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

咱們再回顧 Person 這個類。在構造 Person 的時候,Person 內部初始化了 Driveable 對象,選擇了 Train() 爲實現,這種編碼方式太具備侷限性了。下次選擇其它出行方式如 Bike 或者 Car 的時候,Person 這個類須要修改。

public class Person { private Driveable mDriveable; public Person() { mDriveable = new Bike(); //mDriveable = new Car(); //mDriveable = new Train(); } public void chumen() { System.out.println("出門了"); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

爲了避免由於依賴實現的變更而去修改 Person,也就是說以可能在 Driveable 實現類的改變下不改動 Person 這個類的代碼,儘量減小二者之間的耦合。咱們須要採用上一節介紹的 IoC 模式來進行改寫代碼。

這個須要咱們移交出對於依賴實例化的控制權,那麼依賴怎麼辦?Person 沒法實例化依賴了,它就須要在外部(IoC 容器)賦值給它,這個賦值的動做有個專門的術語叫作注入(injection),須要注意的是在 IoC 概念中,這個注入依賴的地方被稱爲 IoC 容器,但在依賴注入概念中,通常被稱爲注射器 (injector)。

表達通俗一點就是:我不想本身實例化依賴,你(injector)建立它們,而後在合適的時候注入給我吧。

再好比顧客去餐廳須要碗筷,可是顧客不須要本身帶碗筷去,因此,在點菜的時候和服務員說,你給我一副碗筷吧。在這個場景中若是按照正常的編程方式,碗筷自己是顧客的依賴,可是應用 IoC 模式以後 ,碗筷是服務員提供(注入)給顧客的,顧客不用關心吃飯的時候用什麼碗筷,由於吃不一樣的菜品,可能餐具不一樣,吃牛排用刀叉,喝湯用調羹,雖然顧客就餐時須要餐具,可是餐具的配置應該交給餐廳的工做人員。

這裏寫圖片描述

若是以軟件角度來描述,餐具是顧客是依賴,服務員給顧客配置餐具的過程就是依賴注入。

上一節的外賣員和菜品的例子,其實也是依賴注入的例子。

實現依賴注入有 3 種方式: 
1. 構造函數中注入 
2. setter 方式注入 
3. 接口注入

咱們如今一一觀察這些方式

構造函數注入

public class Person { private Driveable mDriveable; public Person(Driveable driveable) { this.mDriveable = driveable; } public void chumen() { System.out.println("出門了"); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

優勢:在 Person 一開始建立的時候就肯定好了依賴。 
缺點:後期沒法更改依賴。

setter 方式注入

public class Person { private Driveable mDriveable; public Person() { } public void chumen() { System.out.println("出門了"); mDriveable.drive(); } public void setDriveable(Driveable mDriveable) { this.mDriveable = mDriveable; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

優勢:Person 對象在運行過程當中能夠靈活地更改依賴。 
缺點:Person 對象運行時,可能會存在依賴項爲 null 的狀況,因此須要檢測依賴項的狀態。

public void chumen() { if ( mDriveable != null ) { System.out.println("出門了"); mDriveable.drive(); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

接口方式注入

public interface DepedencySetter { void set(Driveable driveable); } class Person implements DepedencySetter{ private Driveable mDriveable; public void chumen() { if ( mDriveable != null ) { System.out.println("出門了"); mDriveable.drive(); } } @Override public void set(Driveable driveable) { this.mDriveable = mDriveable; } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

這種方式和 Setter 方式很類似。有不少同窗可能有疑問那麼加入一個接口是否是畫蛇添足呢?

答案確定是否是的,這涉及到一個角色的問題。仍是之前面的餐廳爲例,除了外賣員以外還有廚師和服務員,那麼若是隻有外賣員實現了一個送外賣的接口的話,那麼餐廳配餐的時候就只會把外賣配置給外賣員。

接口的存在,代表了一種依賴配置的能力。

在軟件框架中,讀取 xml 配置文件,或者是利用反射技術讀取註解,而後根據配置信息,框架動態將一些依賴配置給特定接口的類,咱們也能夠說 Injector 也依賴於接口,而不是特定的實現類,這樣進一步提升了準確性與靈活性。

總結

  1. 依賴倒置是面向對象開發領域中的軟件設計原則,它倡導上層模塊不依賴於底層模塊,抽象不依賴細節。
  2. 依賴反轉是遵照依賴倒置這個原則而提出來的一種設計模式,它引入了 IoC 容器的概念。
  3. 依賴注入是爲了實現依賴反轉的一種手段之一。
  4. 它們的本質是爲了代碼更加的「高內聚,低耦合」。

這裏寫圖片描述

這篇文章我運用了大量的比喻來解釋讓給概念,我相信可以加深讀者們對於這些概念的理解。

若是要獲取更全面的信息,你們能夠查看維基百科相關的頁面。 
Dependency inversion principle 
Inversion_of_control 
Dependency injection

下篇文章,我會講解在 Android 開發中很出名的依賴注入框架 Dagger2。

 
 
 

依賴注入和控制反轉的理解,寫的太好了。

 學習過Spring框架的人必定都會聽過Spring的IoC(控制反轉) 、DI(依賴注入)這兩個概念,對於初學Spring的人來講,總以爲IoC 、DI這兩個概念是模糊不清的,是很難理解的,今天和你們分享網上的一些技術大牛們對Spring框架的IOC的理解以及談談我對Spring Ioc的理解。

1、分享Iteye的開濤對Ioc的精彩講解

  首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得很是通俗易懂,如下內容所有來自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846

1.一、IoC是什麼

  Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:

  ●誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對 象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)。

  ●爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。

  用圖例說明一下,傳統程序設計如圖2-1,都是主動去建立相關對象而後再組合起來:

圖1-1 傳統應用程序示意圖

  當有了IoC/DI的容器後,在客戶端類中再也不主動去建立這些對象了,如圖2-2所示:

圖1-2有IoC/DI容器後程序結構示意圖

1.二、IoC能作什麼

  IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。

  其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。

  IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。

1.三、IoC和DI

  DI—Dependency Injection,即「依賴注入」:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

  理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:

  ●誰依賴於誰:固然是應用程序依賴於IoC容器;

  ●爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源;

  ●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;

  ●注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)。

  IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,「依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。

  看過不少對Spring的Ioc理解的文章,好多人對Ioc和DI的解釋都晦澀難懂,反正就是一種說不清,道不明的感受,讀完以後依然是一頭霧水,感受就是開濤這位技術牛人寫得特別通俗易懂,他清楚地解釋了IoC(控制反轉) 和DI(依賴注入)中的每個字,讀完以後給人一種豁然開朗的感受。我相信對於初學Spring框架的人對Ioc的理解應該是有很大幫助的。

2、分享Bromon的blog上對IoC與DI淺顯易懂的講解

2.一、IoC(控制反轉)

  首先想說說IoC(Inversion of Control,控制反轉)。這是spring的核心,貫穿始終。所謂IoC,對於spring框架來講,就是由spring來負責控制對象的生命週期和對象間的關係。這是什麼意思呢,舉個簡單的例子,咱們是如何找女友的?常見的狀況是,咱們處處去看哪裏有長得漂亮身材又好的mm,而後打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,而後嘿嘿……這個過程是複雜深奧的,咱們必須本身設計和麪對每一個環節。傳統的程序開發也是如此,在一個對象中,若是要使用另外的對象,就必須獲得它(本身new一個,或者從JNDI中查詢一個),使用完以後還要將對象銷燬(好比Connection等),對象始終會和其餘的接口或類藕合起來。

  那麼IoC是如何作的呢?有點像經過婚介找女友,在我和女友之間引入了一個第三者:婚姻介紹所。婚介管理了不少男男女女的資料,我能夠向婚介提出一個列表,告訴它我想找個什麼樣的女友,好比長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,而後婚介就會按照咱們的要求,提供一個mm,咱們只須要去和她談戀愛、結婚就好了。簡單明瞭,若是婚介給咱們的人選不符合要求,咱們就會拋出異常。整個過程再也不由我本身控制,而是有婚介這樣一個相似容器的機構來控制。Spring所倡導的開發方式就是如此,全部的類都會在spring容器中登記,告訴spring你是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。

2.二、DI(依賴注入)

  IoC的一個重點是在系統運行中,動態的向某個對象提供它所須要的其餘對象。這一點是經過DI(Dependency Injection,依賴注入)來實現的。好比對象A須要操做數據庫,之前咱們老是要在A中本身編寫代碼來得到一個Connection對象,有了 spring咱們就只須要告訴spring,A中須要一個Connection,至於這個Connection怎麼構造,什麼時候構造,A不須要知道。在系統運行時,spring會在適當的時候製造一個Connection,而後像打針同樣,注射到A當中,這樣就完成了對各個對象之間關係的控制。A須要依賴 Connection才能正常運行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3以後一個重要特徵是反射(reflection),它容許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是經過反射來實現注入的。

  理解了IoC和DI的概念後,一切都將變得簡單明瞭,剩下的工做只是在spring的框架中堆積木而已。

3、我對IoC(控制反轉)和DI(依賴注入)的理解

  在平時的java應用開發中,咱們要實現某一個功能或者說是完成某個業務邏輯時至少須要兩個或以上的對象來協做完成,在沒有使用Spring的時候,每一個對象在須要使用他的合做對象時,本身均要使用像new object() 這樣的語法來將合做對象建立出來,這個合做對象是由本身主動建立出來的,建立合做對象的主動權在本身手上,本身須要哪一個合做對象,就主動去建立,建立合做對象的主動權和建立時機是由本身把控的,而這樣就會使得對象間的耦合度高了,A對象須要使用合做對象B來共同完成一件事,A要使用B,那麼A就對B產生了依賴,也就是A和B之間存在一種耦合關係,而且是緊密耦合在一塊兒,而使用了Spring以後就不同了,建立合做對象B的工做是由Spring來作的,Spring建立好B對象,而後存儲到一個容器裏面,當A對象須要使用B對象時,Spring就從存放對象的那個容器裏面取出A要使用的那個B對象,而後交給A對象使用,至於Spring是如何建立那個對象,以及何時建立好對象的,A對象不須要關心這些細節問題(你是何時生的,怎麼生出來的我可不關心,能幫我幹活就行),A獲得Spring給咱們的對象以後,兩我的一塊兒協做完成要完成的工做便可。

  因此控制反轉IoC(Inversion of Control)是說建立對象的控制權進行轉移,之前建立對象的主動權和建立時機是由本身把控的,而如今這種權力轉移到第三方,好比轉移交給了IoC容器,它就是一個專門用來建立對象的工廠,你要什麼對象,它就給你什麼對象,有了 IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,經過IoC容器來創建它們之間的關係。

  這是我對Spring的IoC(控制反轉)的理解。DI(依賴注入)其實就是IOC的另一種說法,DI是由Martin Fowler 在2004年初的一篇論文中首次提出的。他總結:控制的什麼被反轉了?就是:得到依賴對象的方式反轉了。

4、小結

  對於Spring Ioc這個核心概念,我相信每個學習Spring的人都會有本身的理解。這種概念上的理解沒有絕對的標準答案,仁者見仁智者見智。若是有理解不到位或者理解錯的地方,歡迎廣大園友指正!

相關文章
相關標籤/搜索