依赖注入

it2022-05-05  95

依赖注入

什么是依赖注入

依赖注入就是把本来应该在程序中有的依赖在外部注入到程序之中。

假定有接口A和A的实现B,那么就会执行这一段代码A a=new B();这个时候必然会产生一定的依赖,然而出现接口的就是为了解决依赖的,但是这么做还是会产生耦合,我们就可以使用依赖注入的方式来实现解耦。

传统方式:

1 public class PersonAppService 2 { 3 private IPersonRepository _personRepository; 4 5 public PersonAppService() 6 { 7 _personRepository = new PersonRepository(); 8 } 9 10 public void CreatePerson(string name, int age) 11 { 12 var person = new Person { Name = name, Age = age }; 13 _personRepository.Insert(person); 14 } 15 } View Code

PersonAppService使用PersonRepository插入Person到数据库。这段代码的问题:

PersonAppService通过IPersonRepository调用CreatePerson方法,所以这方法依赖于IPersonRepository,代替了PersonRepository具体类。但PersonAppService(的构造函数)仍然依赖于PersonRepository。组件应该依赖于接口而不是实现。这就是所谓的依赖性倒置原则。如果PersonAppService创建PersonRepository本身,它成为依赖IPersonRepository接口的具体实现,不能使用另一个实现。因此,此方式的将接口与实现分离变得毫无意义。硬依赖(hard-dependency)使得代码紧密耦合和较低的可重用。我们可能需要在未来改变创建PersonRepository的方式。即,我们可能想让它创建为单例(单一共享实例而不是为每个使用创建一个对象)。或者我们可能想要创建多个类实现IPersonRepository并根据条件创建对象。在这种情况下,我们需要修改所有依赖于IPersonRepository的类。有了这样的依赖,很难(或不可能)对PersonAppService进行单元测试。

为了克服这些问题,可以使用工厂模式。因此,创建的仓储类是抽象的。看下面的代码:

1 public class PersonAppService 2 { 3 private IPersonRepository _personRepository; 4 5 public PersonAppService() 6 { 7 _personRepository = PersonRepositoryFactory.Create(); 8 } 9 10 public void CreatePerson(string name, int age) 11 { 12 var person = new Person { Name = name, Age = age }; 13 _personRepository.Insert(person); 14 } 15 }

       PersonRepositoryFactory是一个静态类,创建并返回一个IPersonRepository。这就是所谓的服务定位器模式。以上依赖问题得到解决,因为PersonAppService不需要创建一个IPersonRepository的实现的对象,这个对象取决于PersonRepositoryFactory的Create方法。但是,仍然存在一些问题:

此时,PersonAppService取决于PersonRepositoryFactory。这个已经是比较好的方法,但还是有一个硬依赖(hard-dependency)。为每个库或每个依赖项乏味的写一个工厂类/方法。测试性依然不好,由于很难使得PersonAppService使用mock实现IPersonRepository。

解决方案 

有一些最佳实践(模式)用于类依赖。

构造函数注入

重写上面的例子,如下所示:

1 public class PersonAppService 2 { 3 private IPersonRepository _personRepository; 4 5 public PersonAppService(IPersonRepository personRepository) 6 { 7 _personRepository = personRepository; 8 } 9 10 public void CreatePerson(string name, int age) 11 { 12 var person = new Person { Name = name, Age = age }; 13 _personRepository.Insert(person); 14 } 15 }

这被称为构造函数注入。现在,PersonAppService不知道哪些类实现IPersonRepository以及如何创建它。谁需要使用PersonAppService,首先创建一个IPersonRepository PersonAppService并将其传递给构造函数,如下所示:

var repository = new PersonRepository(); var personService = new PersonAppService(repository); personService.CreatePerson("Yunus Emre"19);

构造函数注入是一个完美的方法,使一个类独立创建依赖对象。但是,上面的代码有一些问题:

创建一个PersonAppService变得困难。想想如果它有4个依赖,我们必须创建这四个依赖对象,并将它们传递到构造函数PersonAppService。从属类可能有其他依赖项(在这里,PersonRepository可能有依赖关系)。所以,我们必须创建PersonAppService的所有依赖项,所有依赖项的依赖关系等等. .如此,依赖关系使得我们创建一个对象变得过于复杂了。

幸运的是,依赖注入框架自动化管理依赖关系。

属性注入

构造函数注入模式是一个完美的提供类的依赖关系的方式。通过这种方式,您不用创建类的实例,而不会产生依赖项。

但是,在某些情况下,一些类依赖别的类实现某些非必须的功能(这通常适用于切面如日志记录)。一个类实现了一些业务功能,但是我们还希望他实现日志功能。但是我们的日志实现模型并不确定。在这种情况下,我们可以定义依赖为公共属性,而不是让他们放在构造函数。如果我们想在PersonAppService实现日志功能。我们可以重写类如下:

1 public class PersonAppService 2 { 3 public ILogger Logger { get; set; } 4 5 private IPersonRepository _personRepository; 6 7 public PersonAppService(IPersonRepository personRepository) 8 { 9 _personRepository = personRepository; 10 Logger = NullLogger.Instance;//是一个单例对象,实现了ILogger接口,但实际上什么都没做 11 } 12 13 public void CreatePerson(string name, int age) 14 { 15 Logger.Debug("Inserting a new person to database with name = " + name); 16 var person = new Person { Name = name, Age = age }; 17 _personRepository.Insert(person); 18 Logger.Debug("Successfully inserted!"); 19 } 20 }

要想实现日志功能,我们只需要为PersonAppService实例设置了Logger,如下面:

   var personService = new PersonAppService(new PersonRepository()); personService.Logger = new Log4NetLogger(); personService.CreatePerson("Yunus Emre", 19);

Log4NetLogger实现ILogger,使得我们的PersonAppService可以写日志。

如果我们不设置Logger,PersonAppService就不写日志,只是执行了个空函数。因此,我们可以说ILogger实例是PersonAppService 的一个可选的依赖。

几乎所有的依赖注入框架都支持属性注入模式

依赖注入框架

有许多依赖注入框架,都可以自动解决依赖关系。他们可以创建所有依赖项(递归地依赖和依赖关系)。所以你只需要根据注入模式写类和类构造函数&属性,其他的交给DI框架处理!

目前主流的框架有Autofac、Castle Windsor、Unity,Ninject,StructureMap框架等。

posted on 2018-01-16 16:11 tianyamoon 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/tianyamoon/p/8297194.html


最新回复(0)