大數據

面試問到IOC我該如何回答呢?

前言

  相信在面試中,只要問到Spring,基本都會拋出一個問題,說說你對Spring IOC理解吧?雖然在日常的開發經常會使用到,但是要回答起來,並不簡單。大腦經過簡單的頭腦風暴後,蹦出了控制反轉、依賴注入這樣的詞語。顯然這些並不是面試官想聽的。

IOC是什麼?

  IOC(Inverse of Contro)控制反轉,有時候也被稱為DI(Dependency injection)依賴注入,它是一種降低對象耦合關係的一種設計思想。

  2004年,Martin Fowler探討了一個問題,既然IOC是控制反轉,那麼到底是哪些方面的控制被反轉了呢?,經過詳細地分析和論證後,他得出了答案:獲得依賴對象的過程被反轉了。控制被反轉之後,獲得依賴對象的過程由自身管理變為了由IOC容器主動注入。於是,他給“控制反轉”取了一個更合適的名字叫做“依賴注入(Dependency Injection)”。他的這個答案,實際上給出了實現IOC的方法:注入。所謂依賴注入就是:由IOC容器在運行期間,動態地將某種依賴關係注入到對象之中

  控制反轉(IOC)是一種思想,而依賴注入(Dependency Injection)則是實現這種思想的方法。

我的理解

  假如有這樣一個場景,你一時興起你想玩GTA5,這時候你需要先去下載GTA5,然後安裝好GTA5,安裝完以後你就能開心的玩耍了。玩了一段時間你可以能覺得有點膩了,又想玩CS了,很顯然你又要先去下載,然後安裝,才能愉快的玩耍。多經歷幾次,你可能就覺得有點煩了,你就在想要是有個遊戲倉庫就好了,能自動的幫我下載和安裝遊戲。這樣我想玩啥,遊戲倉庫直接給我就可以了。而IOC就是這個遊戲倉庫。

Game

public interface Game {

    void download();

    void install();

    void play();

}

Cs

public class Cs implements Game {
    @Override
    public void download() {
        System.out.println("下載Cs");
    }

    @Override
    public void install() {
        System.out.println("安裝Cs");
    }

    @Override
    public void play() {
        System.out.println("我在玩Cs");
    }
}

Gta5

public class Gta5 implements Game {

    @Override
    public void download() {
        System.out.println("下載GTA5");
    }

    @Override
    public void install() {
        System.out.println("安裝GTA5");
    }

    @Override
    public void play() {
        System.out.println("GTA5玩的很開心");
    }
}

Player

public class Player {

    public void play(){
        Game game = new Gta5();
        game.download();
        game.install();
        game.play();
    }

}

  上面代碼中可以看到PlayerGta5(這可以是任意一個實現了Game接口的類型)之間存在強耦合關係,並且在編譯期間就指定好了。當Gta5發生改變時,Player也需要做出相應的改變。由於每個玩家玩的遊戲都是不一樣的,如果要適應需求,那我們需要不停的進行修改。顯然這是不符合規範的。

public class Player {

    private Game game;.

    public Player(Game game) {
        this.game = game;
    }

    public void play(){
        game.play();
    }

}

  將Player的代碼根據依賴倒置原則,程序要依賴於抽象接口,不要依賴於具體實現進行修改。這樣大大降低了Player和具體Game的實現類的耦合度。這個時候我們可以發現,原本需要在Player內對Game具體實現類進行實例化,現在變成了由外部傳入。假如我傳個Gat5進去,他就在玩Gta5,把Gta5變成Cs,他就在玩Cs了。Player不需要進行任何改變。

image

這個時候,我們再回過頭來看看的定義。

  • 控制反轉:獲得依賴對象的過程由自身管理變為了由IOC容器主動注入。
  • 依賴注入:由IOC容器,在運行期間,動態地將某種依賴關係注入到對象之中。

白話一下

  原本呢,我想玩遊戲,我必須要先去下載好遊戲,等到安裝完成以後,才能開始玩。有了遊戲倉庫以後,我只需要告訴它,我玩啥遊戲就可以了,它就會幫我下載並安裝好遊戲,等到我想玩的時候就能直接玩了。

  原本呢,我需要在Player內自己的去實例化Game的實現類。現在呢,只需要在XML內配置好相應的依賴關係。假如配置的是Gta5。等到Player被實例化的時候,IOC就會將Gta5注入進來了。至於Gta5是如何被實例化的Player完全不需要關心。

概括一下:就是主動創建對象過程變成了被動接收,編譯期依賴變成了運行時依賴,從而達到了對象之間的鬆耦合。

為什麼要使用IOC?好處在哪裡?

  很顯然,IOC的作用是降低對象和對象之間的耦合度,這和我們所期望高內聚,低耦合的設計思想是一致的嘛,所以能降低耦合當然要使用啊。好處有如下幾點:

  • 將類實例化的過程透明化,方便調用方使用。
  • IOC容器使用單例模式管理對象,效率高,可以減少內存的佔用。當然也通過配置可以實現多例。
  • 依賴關係統一管理,方便修改。

IOC和工廠模式的區別?

  不知道會不會有小夥伴吐槽,看了半天IOC不就是個工廠嘛,都是將類實例化的過程透明化,方便調用方使用。其實還是有所區別。當需求發生改變的時候,工廠模式需要修改相應的類才能實現,然而IOC是通過反射機制來實現的,不需要我們重新編譯代碼,因為它的對象都是動態生成的。

  舉個簡單的例子,假如xx類的構造參數發生改變了,工廠就必須要修改對應的創建過程。然而IOC就沒有這個煩惱了,修改相應的配置就可以了,代碼完全不需要進行改動。

<bean id="gta5" class="com.hxh.service.impl.Gta5">
    <constructor-arg name="username" value="不一樣的科技宅"></constructor-arg>
</bean>

我們可以這樣回答。

  IOC翻譯過來的意思是控制反轉,也被稱作為依賴注入。通過將主動創建對象過程變成了被動接收,編譯期依賴變成了運行時依賴,以此來降低對象之間的耦合度。為了實現依賴注入,需要在XML內配置好依賴關係,並且將對象實例化,銷燬,等過程統一交由IOC容器進行管理。這樣的話,由於IOC容器將類的實例化過程透明化,並且創建的是單例對象,所以在方便調用方的使用同時,還減少了內存的佔用。

參考文章

結尾

  寫完內心有點忐忑,如果有覺得寫的不對的地方,希望能在評論區指出來,感謝。

  如果覺得對你有幫助,可以多多評論,多多點贊哦,也可以到我的主頁看看,說不定有你喜歡的文章,也可以隨手點個關注哦,謝謝。

  我是不一樣的科技宅,每天進步一點點,體驗不一樣的生活。我們下期見!

Leave a Reply

Your email address will not be published. Required fields are marked *