之前一直不理解IOC DI,今天使勁研究了下,感覺朦朦朧朧有點感覺了,網上的這篇文章對我的有很大的啟發
http://www.cnblogs.com/jin-yuan/p/3823559.html
我仔細學習了后,按照自己的習慣從頭到尾自己敲了個實例,最后能跑起來了,感覺特高興,除了用來理解IOC和DI思想,基本沒考慮其他,但是還是貼出來記錄下吧
1,我們先實現一個簡單的讀取數據庫的功能,由於懶得真的去讀數據庫了,直接模擬了,首先是一個簡單的實體類User
namespace ConsoleApp1 { public class User { public int UserID { get; set; } public string UserName { get; set; } } }
2,然后模擬一個空的DBHelper,只是用來感受IOC的方便,沒有真正實現效果,因為要依賴抽象,所以下面的類基本每個都定義了一個接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { public interface IDBHelper { int Execute(string sql); } /// <summary> /// 模擬的DBHelper /// </summary> public class DBHelper : IDBHelper { private ILogHelper logHelper; public DBHelper(ILogHelper logHelper) { this.logHelper = logHelper; } public int Execute(string sql) { logHelper.Info("執行sql:" + sql); return 1; } } }
3,DBHelper里面有個LogHelper只是一個輸出類,模擬工具類,也是為了體驗DI的便利性
using System; namespace ConsoleApp1 { public interface ILogHelper { void Info(string msg); } public class LogHelper : ILogHelper { public void Info(string msg) { Console.WriteLine("info: " + msg); } } }
4,然后是模擬的數據訪問類,里面用集合模擬數據庫
using System.Collections.Generic; namespace ConsoleApp1 { public interface IUserDAL { int Add(User user); List<User> GetUsers(); } public class UserDAL : IUserDAL { private IDBHelper dbHelper; public UserDAL(IDBHelper dbHelper) { this.dbHelper = dbHelper; } public static List<User> users = new List<User>() { new User(){ UserID = 1, UserName ="張三" }, new User(){ UserID =2, UserName ="李四" } }; public int Add(User user) { dbHelper.Execute("insert into User (UserID,UserName) values (3,'王五')"); users.Add(user); return 1; } public List<User> GetUsers() { return users; } } }
5,然后是業務邏輯類,在里面調用數據訪問類,以及工具類,如果是傳統的寫法,這里就要都new一下,既不美觀又很繁瑣
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { public interface IUserBLL { int Add(User user); List<User> GetUsers(); } public class UserBLL : IUserBLL { private IUserDAL userDAL; private ILogHelper logHelper; public UserBLL(IUserDAL userDAL, ILogHelper logHelper) { this.userDAL = userDAL; this.logHelper = logHelper; } public int Add(User user) { logHelper.Info("UserBLL.Add"); return userDAL.Add(user); } public List<User> GetUsers() { logHelper.Info("UserBLL.GetUsers"); return userDAL.GetUsers(); } } }
6,模擬是實現的DI管理類,為了好理解,我按照最簡單的方式實現的,大佬的例子這里也會考慮IOC,所以比我這復雜一些
using System; using System.Collections.Generic; using System.Reflection; namespace ConsoleApp1 { /// <summary> /// 簡單模擬的DI注入類 /// </summary> public class DIManager { /// <summary> /// 存放關系的容器 /// </summary> private Dictionary<Type, Type> container; public DIManager() { container = new Dictionary<Type, Type>(); } /// <summary> /// 將接口和實現類關聯綁定起來 /// </summary> public void Bind<K, V>() { container.Add(typeof(K), typeof(V)); } /// <summary> /// 獲取泛型類型的對象 /// </summary> public T Get<T>() { return (T)Injection(typeof(T)); } /// <summary> /// 對傳入的類型進行構造函數注入 /// </summary> private object Injection(Type type) { object instance = null; foreach (ConstructorInfo ci in type.GetConstructors()) { //循環類的構造函數 if (ci.GetParameters().Length > 0) { List<object> parameters = new List<object>(); foreach (ParameterInfo pi in ci.GetParameters()) { //循環構造函數的參數 if (container.ContainsKey(pi.ParameterType)) { parameters.Add(Injection(container[pi.ParameterType])); //遞歸實現所有相關注冊過的類型的構造函數注入 } } instance = CreateInstance(type, parameters.ToArray()); break; } } if (instance == null) { instance = CreateInstance(type); } return instance; } /// <summary> /// 創建對象 /// </summary> private object CreateInstance(Type type, params object[] args) { return Activator.CreateInstance(type, args); } } }
7,最后是Program的使用,我們只要在程序運行的時候,注冊綁定需要用到的抽象和實現,然后就能直接通過Get獲取實例,並且這些實例中的構造函數都會自動創建注入相關的對象,這樣就不用我們各種重復的new了
using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { DIManager manager = new DIManager(); manager.Bind<IUserBLL, UserBLL>(); manager.Bind<IUserDAL, UserDAL>(); manager.Bind<IDBHelper, DBHelper>(); manager.Bind<ILogHelper, LogHelper>(); IUserBLL userBLL = manager.Get<UserBLL>(); User user = new User() { UserID = 3, UserName = "王五" }; Console.WriteLine(userBLL.Add(user)); foreach (var u in userBLL.GetUsers()) { Console.WriteLine(u.UserName); } Console.ReadKey(); } } }
總結:IOC是控制反轉,就是把底層的耦合拋到外面,類的內部只依賴抽象,代碼里定義的那么多接口就是實現這個效果,但是即使我們把控制拋到了外面,這些對象還是得創建啊,所以就用到了DI(依賴注入)上面的類里面都是通過構造函數來獲取我們要用到得對象,我們依賴這些對象,對象哪來的?答,注冊接口和類得關系,然后在代碼里自動生成的,你可以觀察DIManager的Injection,大致就是根據類的類型獲取構造函數信息,創建構造函數的參數類型的對象,然后根據構造函數以及參數對象創建本身的對象來達到注入的效果,最后遞歸注入所有相關的構造函數(這里貌似性能浪費啊,假如我只使用UserBLL的一個方法,而這個方法有咩有真正的使用UserDAL和LogHelper,那么程序就創建了白創建了2個用不到的對象,不知道真正的DI是不是解決了這個問題呢)
依然是朦朦朧朧,繼續研究!