《依賴注入 初相見》

不詩意的女程序媛不是好廚師~
轉載請註明出處,From李詩雨—https://blog.csdn.net/cjm2484836553/article/details/104449190

在這裏插入圖片描述
上篇我們學習了註解的基本知識,今天我們再來學習一下依賴注入。

當讓我們也不是一下子就來說依賴注入,而是秉着循序漸進的原則,一點一點的理解。

那就讓我們開始吧~

1.什麼是依賴(Dependency)?

通俗點來講 依賴 就是一種需要。

依賴是類與類之間的連接,依賴關係表示一個類依賴於另一個類的定義。

【舉個栗子】:

例如一個人(Person)可以買車(Car)和房子(House),那Person類就依賴於Car類和House類。

public static void main(String[] args) {
    Person person = new Person();
    person.buy(new House());
    person.buy(new Car());
}

static class Person {
    //表示依賴House
    public void buy(House house) {
        System.out.println("買了套房");
    }

    //表示依賴Car
    public void buy(Car car) {
        System.out.println("買了輛車");
    }
}

static class House {
}

static class Car {
}

嗯吶,瞭解了依賴是什麼,接下來繼續下一個概念:依賴倒置 ~

2.什麼是 依賴倒置 ?

2.0 順序依賴

有時候理解一個東西,通過反面來理解也不失爲一種好的方法。

那依賴倒置的反面就是不依賴倒置了,就是順序依賴了。

什麼是順序依賴呢?

比如說:
在這裏插入圖片描述

人的出行依賴於車子,那如果出行由自行車變成了汽車,人對應的類就要改變;如果又變成了火車,人對應的類又要改變。

人依賴於車子,車子一變,人就要跟着改變,這種就叫順序的依賴。

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 goOut() {
        System.out.println("依賴於車子要出門了~");
        mBike.drive();
        //mCar.drive();  //①跟着變
        //mTrain.drive();  //②跟着變
    }

    public static void main(String... args) {
        Person person = new Person();
        person.goOut();
    }
}

好的,瞭解了順序的依賴,那依賴的倒置就是反過來嘍,那就讓我再來繼續看看吧~

2.1依賴倒置的定義

依賴倒置是面向對象設計領域的一種軟件設計原則。

依賴倒置原則(Dependence Inversion Principle,簡稱DIP)

  • 核心思想:高層模塊不應該依賴底層模塊,二者都該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象;
  • 說明:高層模塊就是調用端,低層模塊就是具體實現類。抽象就是指接口或抽象類。細節就是實現類。
  • 通俗來講: 依賴倒置原則的本質就是通過抽象(接口或抽象類)使個各類或模塊的實現彼此獨立,互不影響,實現模塊間的鬆耦合。
  • 問題描述: 類A直接依賴類B,假如要將類A改爲依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作;假如修改類A,會給程序帶來不必要的風險。
  • 解決方案: 將類A修改爲依賴接口interface,類B和類C各自實現接口interface,類A通過接口interface間接與類B或者類C發生聯繫,則會大大降低修改類A的機率。
  • 好處:依賴倒置的好處在小型項目中很難體現出來。但在大中型項目中可以減少需求變化引起的工作量。使並行開發更友好。

我們現在知道了依賴倒置原則的核心是:

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

那什麼是上層模塊和底層模塊 ,什麼又是抽象和細節呢?

我們來繼續往下看~

2.2上層模塊和底層模塊

就拿一個公司來說吧,它一定有架構的設計、有職能的劃分。按照職能的重要性,自然而然就有了上下之分。並且,隨着模塊的粒度劃分不同這種上層與底層模塊會進行變動,也許某一模塊相對於另外一模塊它是底層,但是相對於其他模塊它又可能是上層。
在這裏插入圖片描述

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

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

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

那麼,映射到我們軟件實際開發中,一般我們也會將軟件進行模塊劃分,比如業務層、邏輯層和數據層。
在這裏插入圖片描述

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

因此,如前面所總結,按照決策能力的高低進行模塊劃分。業務層自然就處於上層模塊,邏輯層和數據層自然就歸類爲底層。

2.3抽象和具體

抽象如其名字一樣,是一件很抽象的事物。

抽象往往是相對於具體而言的,具體也可以被稱爲細節。

比如:

1.交通工具是抽象,而公交車、單車、火車等就是具體了。

2.表演是抽象,而唱歌、跳舞、小品等就是具體。

由此可見,抽象可以是物也可以是行爲。

在我們實際的軟件開發中,抽象有兩種形式:接口 & 抽象類

/**
 * Driveable 是接口,所以它是抽象
 */
public interface Driveable {
    void drive();
}
/**
 * 而 Bike 實現了接口,它被稱爲具體。
 */
public class Bike implements Driveable {
    @Override
    public void drive() {
        System.out.println("Bike drive");
    }
}

2.4依賴倒置的好處

在上述的人與車子的例子中,只要車子的類型一變,Person類就要跟着改動。

那有沒有一種方法能讓 Person 的變動少一點呢?

因爲畢竟我們寫的是最基礎的演示代碼,如果工程大了,代碼複雜了,Person 面對需求變動時改動的地方會更多。

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

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

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

上層模塊不應該依賴底層模塊,它們都應該依賴於抽象。

抽象不應該依賴於細節,細節應該依賴於抽象。

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

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

上層模塊不應該依賴於底層模塊。
在這裏插入圖片描述

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 goOut() {
        System.out.println("依賴於車子要出門了~");
        //======順序的依賴======
        //mBike.drive();
        //mCar.drive();  //①跟着變
        //mTrain.drive();  //②跟着變

        //=====依賴倒置=====
        mDriveable.drive();
    }

    public static void main(String... args) {
        Person person = new Person();
        person.goOut();
    }
}

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

好的,通過上面的講解相信你已經漸漸體會到什麼是依賴倒置了。

但是,在編碼的過程中,我們還是發現它的一個不足,那就是它不符合開閉原則。

每次我們都要修改Person類的內部,那我們能不能再不修改Person類內部的情況下,來實現同樣的功能呢?

答案當時是肯定的,所以接下來我們再來學習一下 控制反轉

3.什麼是控制反轉?

控制反轉( IoC )是 Inversion of Control的縮寫,意思就是對於控制權的反轉。

上面的實例中,Person自己掌控着內部 mDriveable 的實例化。

現在,我們可以更改一種方式。將 mDriveable 的實例化移到 Person 外面。

public class Person2 {
    private Driveable mDriveable;

    public Person2(Driveable driveable) {
        this.mDriveable = driveable;
    }

    public void goOut() {
        System.out.println("出門啦");
        mDriveable.drive();
    }

    public static void main(String... args) {
        Person2 person = new Person2(new Car());//變爲了在Person的外部進行改變
        person.goOut();
    }
}

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

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

這種思想其實就是 IoC,IoC 是一種新的設計模式,它對上層模塊與底層模塊進行了更進一步的解耦。控制反轉的意思是反轉了上層模塊對於底層模塊的依賴控制。

好了,學習了這麼多的概念,現在我們終於有了一定的基礎來學習依賴注入了~

4.什麼是依賴注入

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

爲了不因爲依賴實現的變動而去修改 Person,也就是說以可能在 Driveable 實現類的改變下不改動 Person 這個類的代碼,儘可能減少兩者之間的耦合。我們需要採用上一節介紹的 IoC 模式來進行改寫代碼。

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

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

實現依賴注入有 3 種方式:

  1. 構造函數中注入
  2. setter 方式注入
  3. 接口注入
/**
 * 接口方式注入
 * 接口的存在,表明了一種依賴配置的能力。
 */
public interface DepedencySetter {
    void set(Driveable driveable);
}
public class Person2 implements DepedencySetter {
    //接口方式注入
    @Override
    public void set(Driveable driveable) {
        this.mDriveable = mDriveable;
    }

    private Driveable mDriveable;

    //構造函數注入
    public Person2(Driveable driveable) {
        this.mDriveable = driveable;
    }

    //setter 方式注入
    public void setDriveable(Driveable mDriveable) {
        this.mDriveable = mDriveable;
    }

    public void goOut() {
        System.out.println("出門啦");
        mDriveable.drive();
    }

    public static void main(String... args) {
        Person2 person = new Person2(new Car());
        person.goOut();
    }
}

所以,依賴注入大家要記住的重點就是 : 依賴注入是實現控制反轉的手段。

積累點滴,做好自己~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章