在說依賴注入之前,先了解下什么是接口。 接口的相關規則:
-
1. 接口是一個引用類型,通過接口可以實現多重繼承。
-
2. C#中接口的成員不能有new、public、protected、internal、private等修飾符。
-
3. 接口中只能聲明”抽象”成員,所以不能直接下一步對接口進行實例化(即不能使用 new 操作符聲明一個接口的實例對 象),而不能聲明共有的域或者私有的成員變量。
-
4. 接口聲明不包括數據成員,只能包含方法、屬性、事件、索引等成員。
-
5. 接口名稱一般都以“I”作為首字母(當然不這樣聲明也可以),這也是接口和類的一個區別之一。
-
6. 接口成員的訪問級別是默認的(默認為public),所以在聲明時不能再為接口成員指定任何訪問修飾符,否則 編譯器會報錯。
-
7. 接口成員不能有static、abstract、override、virtual修飾符,使用new修飾符不會報錯,但會給出警告說不需要關鍵字new。
-
8. 在聲明接口成員的時候,不准為接口成員編寫具體的可執行代碼,也就是說,只要在對接口進行聲明時指明接口的成員名稱和參數就可以了。
-
9. 接口一旦被實現,實現類必須實現接口中的所有成員,除非實現類本身是抽象類(通過具體的可執行代碼實現接口抽象成員的操作)。
很多時候看到這么多的概念,也是雲里霧里的。項目中的接口使用也是按照老代碼依葫蘆畫瓢。如果是自己練手的代碼或者demo,也是沒有使用接口。(給自己的借口就是,我只是做些小的東西,根本就不需要使用接口一樣可以跑很溜啊。)
接口是什么?(說說我自己的理解,不一定對)
接口就是為了更換一個可能過時或者錯誤的實現而准備的。就像我們的電腦,里面就到處都是接口。usb、內存條、硬盤、電池、鍵盤…等等都是有各自的接口。我們可以通過硬盤接口換個更大的硬盤或者換個更快的固態硬盤。如果鍵盤壞了,也可以通過鍵盤接口買個新的鍵盤換上去。這就是接口明顯的好處。接口也可以理解成大家的約定。約定了特定接口的大小功能等等。
那么我們寫代碼也是一樣,在某些地方可能會經常變動,邏輯會經常修改的地方使用接口約定。下面我們就用硬盤的接口來做示例吧。
首先定義一個硬盤接口。(一個name屬性,一個讀一個寫的方法)
// 硬盤接口 interface IHardDisk { string name { get; } //硬盤的名字屬性 void read(); //讀取數據方法 void write(string str); //寫數據 }
然后我們買了一個200G的硬盤,它實現了上面的接口。
public class HardDisk200 : IHardDisk { public string name { get { return '我是200G硬盤'; } } public void read() { Console.WriteLine('我可以寫入數據哦....'); } public void write(string str) { Console.WriteLine(str); } }
在電腦中使用這個硬盤。
static void Main(string[] args) { //這里的h就是一個插在接口上的設備 IHardDisk h = new HardDisk200(); h.read(); h.write(h.name + ',我可以寫入數據哦'); Console.ReadKey(); }
某天,我們發現這個硬盤太小了,需要換個1T的。(那樣我們可以存很多很多的電影>_),那么買吧。
public class HardDisk1T : IHardDisk { public string name { get { return '我是1T硬盤'; } } public void read() { Console.WriteLine('我可以寫入數據哦....'); } public void write(string str) { Console.WriteLine(str); } }
然后怎么使用了?只要在電腦上的接口直接插上新的硬盤就ok了,其他的什么地方都不用改。
這就是使用接口的好處。當某天我們發現電腦太慢了,我們可以買個固態硬盤,直接在接口使用的地方換上就可以了,其他地方完全不用修改。
這樣,我們就可以在不同時期或不同情況下靈活更換繼承實現了接口的任何對象,而不用修改其它地方的代碼。
又或者說,實現了這個接口的設備就是存儲設備。(它一定有存也一定可以儲,也就是一定可以寫入和讀出數據。)
在我們了解了什么是接口之后,我們接着來說說今天主要的主題吧。
還是先從例子入手,且是我們學過編程都知道的例子,三層。(什么?你不知道什么是三層?那你別看了,先補習了再過來)
我們先來寫個簡單的三層偽代碼。
//DAL: public class DALMsSqlHelper { public int add(string str) { // 省略具體實現 return 1; } // 省略具體實現,如修改 刪除 查詢 } //BLL: public class BLLAddStudent { DALMsSqlHelper mssql = null; public BLLAddStudent() { mssql = new DALMsSqlHelper(); } public int addStudent() { string str = ''; //...省略具體實現 return mssql.add(str); } } //UI: public class UI { BLLAddStudent s = new BLLAddStudent(); public UI() { s.addStudent(); } }
應該說簡單得不能在簡單的三層。
就在系統用了一年之后,老板說:”聽說oracle很牛逼,大公司都是用的oracle。咱們也換上吧。“。 好,那就換吧。
//DAL: public class DALOracleSqlHelper { public int addOracle(string str) { // 省略具體實現 return 1; } // 省略具體實現,如修改 刪除 查詢 } 顯然BLL也要進行修改,因為BLL引用了DAL的查詢類。 //BLL: public class BLLAddStudent { DALOracleSqlHelper mssql = null; public BLLAddStudent() { mssql = new DALOracleSqlHelper(); } public int addStudent() { string str = ''; // 省略具體實現 return mssql.addOracle(str); } }
不就換個數據庫嗎?為何修改這么大,要是老板哪天又要換回oracle怎么辦?這得好好想個辦法。
首先,我們定義一個數據訪問的接口。
public interface ISqlHelper { int add(); // 省略具體實現,如修改 刪除 查詢 } //BAL修改如下: public class DALMsSqlHelper : ISqlHelper { public int add(string str) { // 省略具體實現 return 1; } // 省略具體實現,如修改 刪除 查詢 } public class DALOracleSqlHelper : ISqlHelper { public int addOracle(string str) { // 省略具體實現 return 1; } // 省略具體實現,如修改 刪除 查詢 public int add(string str) { // 省略具體實現 return 1; } } //BLL: public class BLLAddStudent { ISqlHelper mssql = null; public BLLAddStudent(ISqlHelper sqlhelper) { mssql = sqlhelper; } public int addStudent() { string str = ''; //...省略具體實現 return mssql.add(str); } } //UI: public class UI { public UI() { ISqlHelper sqlhelper = new DALOracleSqlHelper(); BLLAddStudent s = new BLLAddStudent(sqlhelper); s.addStudent(); } }
如果哪天老板又要換會mssql怎樣辦。那么僅僅只要修改UI
又過一年之后,因為公司不景氣。所以又來需求了。老板:”唉,算了。我們還是用mysql吧。免費的,為公司節省點“。那么我們又要修改了。
首先需要重新寫個mysql的實現。
//DAL: public class DALMySqlHelper : ISqlHelper { public int add(string str) { // 省略具體實現 return 1; } // 省略具體實現,如修改 刪除 查詢 } //UI實現如下: public class UI { public UI() { ISqlHelper sqlhelper = new DALMySqlHelper(); BLLAddStudent s = new BLLAddStudent(sqlhelper); s.addStudent(); } }
我們有沒有發現。我們只是在DAL新增了一個mysql的實現和修改了下UI層的接口構造。其中BLL我們根本就沒有動它的。
是的,這樣我們就可以說這里的UI對於BLL來說就是”依賴注入“,BLL對於UI來說就是”控制反轉“。所以,我覺得依賴注入和控制反轉是同一個概念,只是立場不同。
上面,我們看到了雖然BLL層已經不需要變動就可以新增一個數據源的訪問。那么我們能不能也不修改UI層呢? 這里就可以用到反射了。
然后,不管老板想怎么折騰,我只需要改改配置文件就可以了。甚至都不用動代碼。(如果需要新增一個數據源操作,也只要重新實現下,然后改改配置文件)。