1. 程式人生 > >控制反轉 vs 依賴注入

控制反轉 vs 依賴注入

一、介紹

ioc(控制反轉)意味物件不用自己建立需要的物件,相反,它們直接從其他地方獲得需要的物件。DI(依賴注入)是一個IoC的具體實現,即在執行時通過不同的注入技術,如setter注入、constructor注入或介面注入來提供物件依賴。由於DI是IoC的具體實現,因此看起來兩則貌似一致,但是它們的側重點不同,IoC側重物件的建立過程,即物件建立建立的控制權交給了其他地方,而DI側重依賴的注入方式,即通過不同的技術動態注入依賴。

在spring中,ioc和DI經常會混為一談,因為,spring文件自己都說Ioc也被稱為DI,但是在spring中,Ioc更多的是用來使能DI的。

二、控制反轉

ioc可以這麼理解,其他人來幫你建立物件,而不是自己手動編碼“new MyObject”。這個其他人一般就是IOC容器,ioc容器來幫你建立物件,而ioc容器不只是建立物件,還會管理物件的宣告週期,每個不同的生命週期都會被ioc容器呼叫不同的方法。一些ioc容器,比如spring容器、java Servlets容器和Akka Actors。spring容器不僅負責bean的建立,還控制bean的生命週期。servlets容器也是負責建立servlet和管理他的生命週期

儘管通過IOC,讓出了物件建立的控制權,但是還是需要提供模板來讓容器建立物件,如下面所示:

IoC Container Managed Objects Name Managed Objects Definition
Spring Container Bean Classes defined with annotations/XML configuration
Servlet Container Servlet Classes implementing interface Servlet
Actor System Actor Classes extending trait Actor

三、依賴注入

簡單來說,DI和硬編碼注入是相對的:

//Hardcoded dependency
public class MyClass { 
    private MyDependency myDependency = new MyDependency(); 
}
//Injected dependency
public class MyClass { 
    private MyDependency myDependency;
    
    public MyClass(MyDependency myDependency){
        this.myDependency = myDependency;
    }
}

可以看出,可以通過傳參給建構函式、setter方法來注入依賴。但是DI有個缺點,就是需要管理依賴和手動注入依賴,即建立依賴物件,然後傳參。

下面看一個例子,MyClass1依賴MyClass2,MyClass2依賴MyClass3:

public class MyClass3 {
    public void doSomething(){}
}

//MyClass2 depends on MyClass3
public class MyClass2 {
    private MyClass3 myClass3;

    public MyClass2(MyClass3 myClass3){
        this.myClass3 = myClass3;
    }

    public void doSomething(){
        myClass3.doSomething();
    }
}

//MyClass1 depends on MyClass2
public class MyClass1 {
    private MyClass2 myClass2;

    public MyClass1(MyClass2 myClass2){
        this.myClass2 = myClass2;
    }

    public void doSomething(){
        myClass2.doSomething();
    }
}

public class Main {

    public static void main(String[] args) {

        //All dependencies need to be managed by the developer
        MyClass3 myClass3 = new MyClass3();
        MyClass2 myClass2 = new MyClass2(myClass3);
        MyClass1 myClass1 = new MyClass1(myClass2);

        myClass1.doSomething();
    }
}

繼續下去,如果MyClass2還依賴MyClass4,那麼還需要手動新增新依賴:

public class MyClass2 {
    private MyClass3 myClass3;
    private MyClass4 myClass4;

    public MyClass2(MyClass3 myClass3, MyClass4 myClass4){
        this.myClass3 = myClass3;
        this.myClass4 = myClass4;
    }

    public void doSomething(){
        myClass3.doSomething();
        myClass4.doSomething();
    }
}

public class Main {

    public static void main(String[] args) {

        MyClass4 myClass4 = new MyClass4();
        MyClass3 myClass3 = new MyClass3();
        MyClass2 myClass2 = new MyClass2(myClass3, myClass4);
        MyClass1 myClass1 = new MyClass1(myClass2);

        myClass1.doSomething();
    }
}

即使這樣描述並不是錯的,但是現實中的一個程式成百上千個依賴分散在不同的程式碼區域中,我們需要找到,然後再集中起來建立依賴和管理依賴,如上面的例子似的。很不方便,這就是接下來要討論的。

四、Ioc和DI配合

依賴太多,依賴的建立和依賴注入都很複雜。但是通過IOC,這些依賴可以被IOC容器來管理、建立。通過使用類似@Autowired的註解,容器可以自動在需要的地方注入依賴,因此程式設計師不需要關注依賴的建立和注入。

public class MyClass1 {
    
    @Autowired
    private MyClass2 myClass2;

    public void doSomething(){
        myClass2.doSomething();
    }
}
public class MyClass2 {

    @Autowired
    private MyClass3 myClass3;
    @Autowired
    private MyClass4 myClass4;

    public void doSomething(){
        myClass3.doSomething();
        myClass4.doSomething();
    }
}

上面沒有了物件的建立過程,這是因為ioc容器被提供模板後,會自動建立依賴,比如spring用<bean>標籤定義模板。

參考