控制反转 跟依赖注入,这两个概念很多人搞不清楚,越研究越模糊,研究了之后不知道在哪里用?
一:什么是控制反转
IoC——Inversion of Control 控制反转
DI——Dependency Injection 依赖注入
1:控制反转 不是一种技术,是一种思想
2:依赖注入是 控制反转的 一种具体实现方式,
要想理解上面两个概念,就必须搞清楚如下的问题:
- 参与者都有谁?
- 依赖:谁依赖于谁?为什么需要依赖?
- 注入:谁注入于谁?到底注入什么?
- 控制反转:谁控制谁?控制什么?为何叫反转(有反转就应该有正转了)?
二:那么控制反转跟依赖注入 是干什么的,为什么要有这两个概念?
背景:
做过项目的人,尤其是ERP那样的臃肿的项目的时候,项目迭代了无数次,人员换了一批又一批的时候,新接手的人第一眼看到代码,首先是:100个鄙视,这么烂的代码也写的出来。然后准备去优化,要显示一下自己的水平,准备去修改一点点写的功能的时候发现,这里不改,那里也不能改,补好了一个bug,然后出现了10给bug。这就是项目的维护成本有多高 就可想而知了。
这个时候就知道天天喊得口号:低耦合 高内聚 真的不是喊口号喊出来的。
耦合度高表现在哪里?
1:类与类之间直接调用,直接在A类中直接New一个对象 ,然后调用里面的方法,属性,等
2:项目直接dll引用 (C#)/ 包的导入(Java) 直接引用dll,dll里面的类直接new对象。
3:业务之间的互相调用,逻辑不清晰 等等
那么我们如何解决呢?
那有没有办法,在调用类里面的代码的时候,不直接new对象呢?完全脱离对象实例化呢?答案是 :有办法
这里就需要用到 依赖注入:
原先A类调用B类是: 在A类的代码里面实现:B b=new B(); 然后 b.方法/属性/成员变量 等
用依赖注入怎么处理呢?那就不需要new对象了,只需要用对象继承的接口直接去调用接口里面的方法,整个过程中没有任何关于B类的实例化:
我们知道了依赖注入是干什么的了。目的就是 解耦。实现代码的各个模块之间,各个类之间的分离。
三:依赖注入的三种方式:
1、属性注入 2、构造器注入 3、方法注入
四:依赖注入:是一种实现控制反转的过程,这个过程如何实现呢?
这里我们需要了解一些技术:
例如1:C# 里面需要了解Ninject 容器 Unity容器 /java里面的Spring容器
这里具体各个容器的具体实现,后面有时间再写,这里写一些依赖注入的思想:
相当于有一个大的容器。A类 ,B类 都可以事先把对象通过 接口注入 的方法注入到容器中,就相当于已经实例化了,谁要用谁来直接拿。所以这里依赖注入的一般C#都默认放在:Global.asax 文件中,目的是为了不影响后面的性能。这样在项目启动的时候就已经启动了,不会对后面的业务有任何的性能影响。
以Ninject 为示例:以Factory方式依赖注入
首先1:把接口需要通过一定的方式注入进容器:
前面的一些是ninject内部需要注入的,后面的访问日志,首页 就是业务上需要注入的接口。
kernel.Bind<IDisposable>().To<CL.Service.Disposable>();
kernel.Bind<DB.Interfaces.IDatabaseFactory>().To<DB.Service.DatabaseFactory>();
kernel.Bind<DB.Interfaces.IHttpRuntimeWrapper>().To<DB.Service.HttpRuntimeWrapper>();
kernel.Bind<IDatabaseFactory>().To<CL.Service.DatabaseFactory>();
kernel.Bind(typeof(IRepository<>)).To(typeof(CL.Service.RepositoryBase<>));
kernel.Bind(typeof(DB.Interfaces.IRepository<>)).To(typeof(DB.Service.RepositoryBase<>));
//访问日志
kernel.Bind<CL.Interfaces.VisitLog.IVisitLogRepository>().To<CL.Service.VisitLog.VisitLogRepository>();
//首页banner
kernel.Bind<CL.Interfaces.Banner.IBannerRepository>().To<CL.Service.Banner.BannerRepository>();
//热门城市
kernel.Bind<CL.Interfaces.Search.IHotCityRepository>().To<CL.Service.Search.HotCityRepository>();
2:然后在Global.asax 文件中提前注入完成。
#region 提前注入步骤
//访问日志
RepositoryFactory.VisitLogRepository = RepositoryService.GetService<CL.Interfaces.VisitLog.IVisitLogRepository>();
//首页banner
RepositoryFactory.BannerRepository = RepositoryService.GetService<CL.Interfaces.Banner.IBannerRepository>();
3:调用:直接这样调用各种接口里面的方法。
RepositoryFactory.WeAppCouponActivity.InsertWeAppCouponActivity(rel.WeChatMemberBindInfo.cardNo, m676Info.phone, "WeChatApplet");
后面再详细讨论 各个容器之间的依赖注入方式以及如何实现调用。网上最多的案例就是spring的依赖注入。
五:后续...