我的學習筆記,來自Acode。html
一、術語java
控制反轉/反向控制,英文全稱「Inversion of Control」,簡稱IoC。程序員
依賴注入,英文全稱「Dependency Injection」,簡稱DI。數據庫
聽說是Martin Fowler對控制反轉的原理進行了深刻的探索後,爲控制反轉起了個新的名字叫「依賴注入」。也就是說,這兩個術語講的是同一個事物。函數
二、控制反轉的「奧義」學習
「實現必須依賴抽象,而不是抽象依賴實現」。this
三、實例理解spa
只看了控制反轉的奧義,對於通常人來講仍是難以理解,就比如一位功夫大師告訴咱們「無招勝有招」,資質平平的我仍是沒法理解到大師們的話,因而就凌亂了...code
下面,仍是經過實際的程序來理解一下控制反轉。htm
假設場景:從SQL Server數據庫中獲取數據。
【普通方法】
通常人會這麼寫程序:
首先,實現一個對數據庫進行各類操做的類SqlServerDataBase:
1 /* 2 *SqlServerDataBase.java 3 **/ 4 5 public class SqlServerDataBase { 6 7 /* 8 *構造函數、數據庫鏈接等操做對應的語句 9 */ 10 11 //從SQL Server數據庫中獲取數據 12 public List getDataFromSqlServer() { 13 //獲取數據的具體語句 14 } 15 }
而後,就能夠在業務層經過對象的實例來獲取須要的數據:
1 /* 2 *Business.java 3 **/ 4 5 public class Business{ 6 private SqlServerDataBase db = new SqlServerDataBase(); 7 8 //從SQL Server數據庫中獲取數據 9 public void getData() { 10 List list = db.getDataFromSqlServer(); 11 } 12 13 }
這麼一看,感受代碼寫得很不錯啊,完美地知足了需求。但是,某一天,決定要將數據庫更換爲MySQL,因而咱們歷盡千辛萬苦寫的兩個程序都不能用了,咱們須要重寫下面的代碼:
/* *MySQLDataBase.java **/ public class MySQLDataBase { /* *構造函數、數據庫鏈接等操做對應的語句 */ //從MySQL數據庫中獲取數據 public List getDataFromMySQL() { //獲取數據的具體語句 } } /* *Business.java **/ public class Business{ private MySQLDataBase db = new MySQLDataBase(); //從MySQL數據庫中獲取數據 public void getData() { List list = db.getDataFromMySQL(); } }
咱們又費勁巴拉地寫了上面的兩個文件。這時,客戶又決定要用Oracle做爲數據!!!這時,估計你的心裏是無比的崩潰的。爲了毛爺爺,咱們忍了,再次寫了新的程序。
當寫了以Oracle爲數據庫的程序後,客戶說要用MongoDB做爲數據庫!!!這時,感受不再要當程序員了。
總結:
從上面的例子中能夠看出,一旦客戶的業務需求變了,咱們寫的代碼基本就白搭了,還要重寫新的代碼。這是爲何呢?緣由是:Business類的中使用到了具體的數據庫對應的類,一旦要使用的數據庫改變了,Business中的代碼也得跟着改啊(getDataFromSqlServer()->getDataFromMySQL()->...)。
教訓:
要實現一種代碼能夠屢次重複利用,不依賴於具體業務的代碼,實現Business類的重用。這時,咱們想到了大神告訴咱們的控制反轉的奧義「實現必須依賴抽象,而不是抽象依賴實現」。
【IoC方式實現】
一、首先,定義一個從數據庫中獲取數據的接口DataBase,做爲一個接口,它裏面只有方法的聲明,此處咱們須要一個從數據庫中獲取數據的方法:getData()。
1 /* 2 *DataBase.java 3 */ 4 5 public interface DataBase{ 6 //該方法用來獲取數據 7 public void getData(); 8 }
二、接着,此時客戶要求採用SQL Server做爲項目的數據庫,那麼就實現一個負責從SQL Server數據庫中獲取數據的類SqlServerDataBase:
1 /* 2 *SqlServerDataBase.java 3 */ 4 5 public class SqlServerDataBase implements DataBase{ 6 //該方法用來獲取數據 7 public void getData() { 8 //如下是具體從SQL Server數據庫中取數據的代碼 9 ...... 10 } 11 }
該類具體實現了從SQL Server數據庫中獲取數據的過程。
三、此時,業務須要從數據庫中獲取數據,實現業務類Business(注意該類的寫法,很重要):
1 /* 2 *Business.java 3 */ 4 5 public class Business { 6 //針對接口DataBase定義變量 7 private DataBase db; 8 9 public void setDataBase(DataBase db) { 10 this.db=db; 11 } 12 13 //根據注入的數據庫類,從xxx數據庫中獲取數據 14 public void getData() { 15 db.getData(); 16 } 17 }
注意的地方:
(1)在第7行,定義了一個操做數據庫的接口的變量db,並非依賴於具體實現的類的變量;
(2)定義了setDataBase()方法,該方法實現了具體實現的數據庫操做類的變量的注入;
(3)getData()方法或根據實際注入的數據庫類型獲取數據。
總結:
能夠看出,該方法的實現是依賴於抽象,而非依賴於具體的實現,是否是想到了IoC的終極奧義「實現必須依賴抽象,而不是抽象依賴實現」。
四、經過3中的Business類,實現了代碼的重用(我的感受,也就是對業務層也實現了「兼容性強的一種封裝」,所以才實現了代碼的重用),下面就看一下怎麼根據具體的數據庫類型來實現數據庫的取數據:
1 /* 2 *TestBusiness.java 3 */ 4 5 public class TestBusiness { 6 private Business Business = new Business(); 7 8 //根據注入的數據庫類,從SQL Server數據庫中獲取數據 9 public void getData() { 10 business.setDataBase(new SqlServerDataBase()); 11 business.getData(); 12 } 13 }
在第10行中,咱們將SQL Server類的變量的注入到業務類Business中,也就實現了從SQL Server數據庫中取數據的操做。此時,咱們就不再怕客戶更換數據庫了。
假設客戶要採用MySQL做爲數據庫,那麼,咱們須要作的只有兩點:
(1)實現基於MySQL的取數據的類MySQLDataBase
1 /* 2 *MySQLDataBase.java 3 */ 4 5 public class MySQLDataBase implements DataBase{ 6 //該方法用來獲取數據 7 public void getData() { 8 //如下是具體從MySQL數據庫中取數據的代碼 9 ...... 10 } 11 }
(2)更改注入的數據庫類型
1 /* 2 *TestBusiness.java 3 */ 4 5 public class TestBusiness { 6 private Business Business = new Business(); 7 8 //根據注入的數據庫類,從MySQL數據庫中獲取數據 9 public void getData() { 10 business.setDataBase(new MySQLDataBase()); 11 business.getData(); 12 } 13 }
咱們只須要更改第10行中藥注入的數據庫類型,就完成了全部的工做。
此時,再次理解下IoC的終極奧義:實現必須依賴抽象,而不是抽象依賴實現。(修煉的境界又提高了一層)