第二章 控制反转和依赖注入
Spring.Net 中提供了很多功能,比如依赖注入,面向切面编程(AOP),数据访问抽象以及ASP.Net扩展等等的功能。而其中最核心的功能就是依赖注入(Dependency Injection),而使用依赖注入带来的最大好处就是能够通过它降低应用程序中对象与对象之间的耦合。
控制反转(Inversion of Control)和 依赖注入(Dependency Injection),他们的英文缩写分别是IOC和DI,其实它们是同一个概念的不同角度的描述,由于控制反转概念比较模糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级别人物Martin Flower又给出了一个新名字:”依赖注入”,相对控制反转而言,“依赖注入”又 确描述了被注入对象依赖IOC容器配置以来对象。
IOC 也称为 “依赖倒置原则”("Dependency Inversion Principle") 或 “依赖倒置原则”。在面向对象编辑几大原则中 “依赖倒置原则“是非常重要的,差不多所有框架都使用了“倒置注入“(Martin Fowler 2004年发布的论文中有提到)技巧,这可说是IOC原理的一项应用。C++, Java 或各种.NET 语言等面向对象程序语言的程序员已使用了这些原理。
2.1 熟悉的代码存在着潜在的危机
这是我们以前写代码的方式,这里就使用园子来举个例:
我们现在需要获取园里各位人物的文章,现在提供了一个 取文章的数据访问接口IReaderDAL ,和一个取文章的业务逻辑接口IReaderService , 通过在业务逻辑实现类中调用数据访问实现类的方法来获取文章。
1 namespace CnblogLesson_2_1 2 { 3 /// <summary> 4 /// 读取文章的数据数据访问接口 5 /// </summary> 6 public interface IReaderDAL 7 { 8 /// <summary> 9 /// 读取文章的方法 10 /// </summary> 11 void GetArticle(); 12 } 13 }
1 namespace CnblogLesson_2_1 2 { 3 /// <summary> 4 /// 读取文章的业务逻辑接口 5 /// </summary> 6 public interface IReaderService 7 { 8 /// <summary> 9 /// 读取文章的方法 10 /// </summary> 11 void GetArticle(); 12 } 13 }
1 using System; 2 namespace CnblogLesson_2_1 3 { 4 /// <summary> 5 /// 读取文章的数据访问类 6 /// </summary> 7 public class ReaderDAL : IReaderDAL 8 { 9 /// <summary> 10 ///读取文章的方法 11 /// </summary> 12 public void GetArticle() 13 { 14 Console.WriteLine("取得博客园Spring.Net系列的博文"); 15 } 16 } 17 }
1 using System; 2 namespace CnblogLesson_2_1 3 { 4 /// <summary> 5 /// 读取文章的业务逻辑类 6 /// </summary> 7 public class ReaderService : IReaderService 8 { 9 /// <summary> 10 /// 读取文章的数据访问类对象 11 /// </summary> 12 private IReaderDAL dal = new ReaderDAL(); 13 14 /// <summary> 15 /// 读者的名字 16 /// </summary> 17 private string Name { get; set; } 18 19 /// <summary> 20 /// 读取文章的方法 21 /// </summary> 22 public void GetArticle() 23 { 24 dal.GetArticle(); 25 } 26 } 27 }
上面是博客园读者的数据访问类和业务逻辑类,来看一下以往我们的调用方式。
1 using System; 2 namespace CnblogLesson_2_1 3 { 4 class Program 5 { 6 static void Main(string[] args) 7 { 8 IReaderService service = new ReaderService(); 9 service.GetArticle(); 10 Console.ReadKey(); 11 } 12 } 13 }
以上方式的缺点:
IReaderDAL对象是在 ReaderService内部创建并维护,严重依赖于具体实现,而不是接口。 在不改代码的情况下就非常难以扩展和维护。 例如,我们现在获取园子文章的方式变了,原来使用ReaderDAL的GetArticle方法来获取,现在变成通过WebService来获取,哪又如何是好,只能重新修改ReaderService内部的代码。
解决办法:
使用所谓的Ioc(控制反转),就是应用本身不负责依赖对象的创建和维护(好处就是解耦的各个对象之间相互的依赖),将依赖 对象创建和维护的权力交由外部容器,比如,我们将具体要创建的对象交给配置文件。通过配置文件来决定,从园子获取文章的方式是通过ReaderDAL类还是通过WebService ,当需要切换获取方式的时候只需要修改配置,无需修改代码就可以完成。这样控制权就到了外部容器,这就叫 控制反转。从运行时来看,由外部容器动态的将依赖对象注入到应用程序中。也可以理解为动态的通过xml配置,将获取文章方式传入ReaderService中。这样的方式叫做DI(依赖注入)。
2.2 使用依赖注入和控制反转之后如何解耦
我们在对IReaderDAL对象的创建和维护上做出了改变,改为使用依赖注入的方式,IReaderDAL通过配置文件动态注入到应用程序,或者说是IReaderDAL对象的创建和维护的权力从IReaderService内部转移到了外部,交给配置文件来控制。
这个时候我们来回顾一下2.1章节里对控制反转和依赖注入的描述,我们将更好的理解他们的含义。
1 using Spring.Context.Support; 2 namespace CnblogLesson_2_2 3 { 4 /// <summary> 5 /// 读取文章的业务逻辑类 6 /// </summary> 7 public class ReaderService : IReaderService 8 { 9 /// <summary> 10 /// 读取文章的数据访问类对象 11 /// </summary> 12 private IReaderDAL dal ; 13 14 public ReaderService() { 15 16 //通过IApplicationContext来配置 17 IApplicationContext context = ContextRegistry.GetContext(); 18 dal = (IReaderDAL)context.GetObject("readerDal"); 19 } 20 21 /// <summary> 22 /// 读者的名字 23 /// </summary> 24 private string Name { get; set; } 25 26 /// <summary> 27 /// 读取文章的方法 28 /// </summary> 29 public void GetArticle() 30 { 31 dal.GetArticle(); 32 } 33 } 34 }
朋友们,上面使用到了Spring.Net框架的IOC容器,但这里只讨论依赖注入和控制反转的思想,实际上可以追溯到依赖倒置原则,依赖倒置原则说“抽象不应该依赖于细节。细节应该依赖与抽象 “。目前,暂时不要关心Spring.Net 的IOC容器是怎样配置和搭建起来的,在接下来的章我们会对这些问题一一起讨论。