控制反转和依赖注入,你真的分得清吗?
使用過Spring的老鐵,應該都聽說過 “控制反轉” 和 “依賴注入” ,也就是常說的IOC 和 DI,這兩個概念通常會一起出現,那么這兩者是一個概念嗎?又該如何作區分,接下來我們就來研究一下,他倆到底是什么東西。
在正式開始之前,我先提出幾個問題,然后我們帶著問題開始接下來的研究。
1.控制反轉中的"控制",具體指什么?反轉又是從哪里反轉到了哪里?
2.依賴注入中的依賴是什么?注入又是怎么實現的?
3.依賴注入和控制反轉有什么區別呢?
控制反轉
控制反轉的英文翻譯是 Inversion Of Control,對這個陌生沒關系,它的簡寫你一定很熟悉,那就是IOC,不過這個IOC和Spring中的Ioc容器還不太一樣,后面我們詳細說明。
簡單來說:IOC是一種框架設計思想,而spring的Ioc容器是一個具體的技術實現。
為了更好的說明IOC的思想,我們先看一下下面的例子:
非控制反轉
public class TestCaseOne {public boolean testOne() {return true;} } public class TestCaseTwo {public boolean testTwo() {return true;} } public class Main {public static void main(String[] args) {TestCaseOne one = new TestCaseOne();TestCaseTwo tow = new TestCaseTwo();if(one.testOne()) {System.out.println("test successful.");} else {System.out.println("test failed.");}if(tow.testTwo()) {System.out.println("test successful.");} else {System.out.println("test failed.");}} }控制反轉
public abstract class TestCaseBase {public abstract boolean test(); }public class TestCaseOne extends TestCaseBase{@Overridepublic boolean test() {return true;} }public class TestCaseTwo extends TestCaseBase{@Overridepublic boolean test() {return true;} }public class Application {private static List<TestCaseBase> caseContainer = new ArrayList<>();public static void addTestCase(TestCaseBase testCase) {caseContainer.add(testCase);}public static void runTestCase() {for (TestCaseBase testCase : caseContainer) {if(testCase.test()) {System.out.println("test successful.");} else {System.out.println("test failed.");}}} }public class Main {public static void main(String[] args) {Application.addTestCase(new TestCaseOne());Application.addTestCase(new TestCaseTwo());Application.runTestCase();} }在上面兩段代碼中,對比兩個Main類中的main方法,你能看出什么差異嗎?上面兩段代碼實現的功能是一樣的,都是執行測試用例,不過兩者實現的方式差異卻很大。
在第一段代碼中,每一個測試用例的被測試方法,在main方法中,從上到下逐個被調用,調用代碼是程序員自己編寫的,也就是整個測試用例的執行流程中的“控制流”—— 調用者調用被調用的過程,完全是程序員自己控制的。
而在第二段代碼中,程序員需要做的事情主要是將測試用例添加到測試框架中,然后運行測試框架,具體測試用例中的測試方法以什么樣的方式運行,以及在什么時間點被調用,程序員是不知道的, 也就是測試用例執行流程中的"控制流",不是程序員控制的,而是框架控制的。
到這里,我們就可以回答上面關于控制反轉的兩個問題了。
“控制” 是指對程序執行流程的控制權,也就是調用者,在什么時候以什么方式調用被調用者的流程。
控制反轉是指將流程的控制權從程序員手中轉移到了框架里。
控制反轉有什么好處呢?
使用控制反轉的思想,可以讓程序員更加專注編寫業務代碼,無需關注非業務代碼運行流程的細節,可以提高工作效率,這也是oop中封裝原則的初衷。
控制反轉的思想在絕大部分的框架中都有應用。我們在使用第三方框架時,只需要根據框架的使用說明,將業務相關的代碼"關聯"(類似于依賴注入代碼中的addTestCase)到框架中即可,而無需關心我們的業務代碼,在框架中是何時被調用的。
有些框架將IOC的思想應用的非常好,將具體的"控制流"封裝的很深,只閱讀框架的源碼很難理清楚整個調用流程,還需要結合ide的調試工具的輔助。閱讀過框架源碼的小伙伴一定深有體會吧。
總結來說,控制反轉是一種設計思想,用來指導應用框架的設計,是“道”層面的內容。
依賴注入
依賴注入也是我們經常聽到一個概念,對于不了解它的讀者來說,感覺很高大上,如果你了解它后,就會覺得 “就這?”,不信接著往下看。
依賴注入簡單來說就是:不通過new()方法在類內部創建被依賴的對象,而是將被依賴的對象,在外部創建好,通過依賴對象的構造方法或者其他參數設置方法,將被依賴的類給傳遞進來。
所以開頭關于依賴注入的問題,現在也可以回答了:
依賴:就是 被new出來的對象,或者被需要的組件。
注入:就是一個將被依賴的組件傳遞到被依賴的組件中的過程。
為了更好的說明依賴注入,我們還是寫一段代碼來說明一下。
非依賴注入代碼:
public abstract class Vehicle {public abstract void drive(); }public class Car extends Vehicle {@Overridepublic void drive() {System.out.println("drive car.");} }public class GoHome {private Vehicle vehicle ;public GoHome() {vehicle = new Car();}public void go() {vehicle.drive();} }依賴注入代碼:
public class GoHomeWithDi {private Vehicle vehicle ;public GoHomeWithDi(Vehicle vehicle) {this.vehicle = vehicle;}public void go() {vehicle.drive();} }在上面代碼中,依賴對象是GoHome和GoHomeWithDi,被依賴對象是 vehicle ,在非依賴注入代碼中,vehicle是代碼中寫死的,那么上面代碼表達的語義就是 “只能開車回家”。
而依賴注入的實現方式,是通過依賴對象的構造方法將被依賴對象注入進來。表達的語義是 “想怎么回家就怎么回家”,因為回家的乘坐的交通工具沒有寫死,可以選擇的方式很多。
相比控制反轉,依賴注入是一種具體的編程技巧,是 “術” 層面的內容。
到這里我想你應該可以回答開頭的第三個問題了,控制反轉和依賴注入,不是一個層面的內容。
在實際的工作中,一個項目里可能會有成百上千的類,類之間存在比較復雜的依賴關系,如果這些依賴關系,使用上面依賴注入的實現方式的話,出現錯誤的可能性會比較大,而且維護成本也比較高,再加上依賴注入和具體業務關系也不大,依賴注入的過程完全可以結合控制反轉的思想 “反轉” 給框架來實現。
而Spring的Ioc容器就是一款比較優秀的依賴注入框架。既然如此,為什么spring還自稱是IOC容器(控制反轉容器)呢?
其實spring的容器,被稱為控制反轉容器,還是依賴注入框架都沒有錯,使用控制反轉容器,是描述這個框架的設計思想,將依賴注入的流程反轉給了框架,而稱之為依賴注入框架,是因為spring的這個容器就是為了解決這個依賴注入的問題,用依賴注入框架來描述更具體,也更具有針對性。
總結
以上是生活随笔為你收集整理的控制反转和依赖注入,你真的分得清吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mondrian:建模多值维度属性
- 下一篇: 协调计算机硬件和软件的中间层次,计算机的