之前一直不理解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是不是解决了这个问题呢)
依然是朦朦胧胧,继续研究!