假设我有一个名为ICustomer
的接口,它定义了一个方法startProcessing()
:
interface ICustomer
{
void startProcessing(byte[] data);
}
现在,我有多个具体的类,比如ProductA_User
和ProductB_User
。每个都有不同的属性,但它们都实现了ICustomer
,因为它们都是客户:
class ProductA_User : ICustomer
{
string _name;
IRepository _repo;
public ProductA_User(string name)
{
_name = name;
}
public void startProcessing(byte[] data)
{
Console.WriteLine(_name);
var temp = _repo.Get<VModel>(filter: x.CName == _name).ToList();
}
}
class ProductB_User : ICustomer
{
public void startProcessing(byte[] data)
{
throw new System.NotImplementedException();
}
}
我正在调用一个名为MemberFactory
的工厂类中的共享方法,并基于productType
创建对象。
enum ProductTypeEnum { ProductA, ProductB }
class MemberFactory
{
ICustomer Create(ProductTypeEnum productType)
{
switch (productType)
{
case ProductTypeEnum.ProductA: return new ProductA_User("Steve");
case ProductTypeEnum.ProductB: return new ProductB_User();
default: return null;
}
}
}
我把它作为枚举的参数。由于每个具体类都不同,但实现了ICustomer
,所以我能够返回一个实现ICustomer
的实例。在ProductA_User()
中,我试图注入_repo
,它只是EF回购。
我该怎么办?我不想传递额外的参数(_repo
)来创建类的对象。我怎样才能给我的concreate类注射?
某个地方有一个类需要ICustomer
。您不希望该类了解ICustomer
的任何实现,所以您使用的是MemberFactory
。
但是,如果需要ICustomer
的类必须创建MemberFactory
并像IRepository
一样向其传递参数,那么您又回到了同样的问题。需要ICustomer
的类不应该知道ICustomer
的某些实现需要IRepository
。
对此有很多方法。我会选择一个简单直接的。
首先,为MemberFactory
:创建一个接口
interface IMemberFactory
{
ICustomer Create(ProductTypeEnum productType)
}
更改MemberFactory
的声明,使其实现接口:
class MemberFactory : IMemberFactory
无论在哪里注入MemberFactory
,都应注入IMemberFactory
。现在,依赖于IMemberFactory
的类对其实现一无所知。它只知道它有一个IMemberFactory
并调用它的Create
方法。
然后,将IRepository
注入到实现中,MemberFactory
。
class MemberFactory
{
private readonly IRepository _repository;
public MemberFactory(IRepository repository)
{
_repository = repository;
}
ICustomer Create(ProductTypeEnum productType)
{
switch (productType)
{
case ProductTypeEnum.ProductA: return new ProductA_User("Steve");
case ProductTypeEnum.ProductB: return new ProductB_User();
default: return null;
}
}
}
现在,如果MemberFactory
需要一个IRepository
来创建某些类型的ICustomer
,那么它就有一个。这并不完美。如果我们以这种方式添加越来越多的依赖项,它可能会失控。但这只是一个开始。
然后,无论什么类型需要IMemberFactory
来创建ICustomer
,都要将IMemberFactory
注入:
class SomeClassThatNeedsMemberFactory
{
private readonly IMemberFactory _memberFactory;
public SomeClassThatNeedsMemberFactory(IMemberFactory memberFactory)
{
_memberFactory = memberFactory;
}
}
现在每个类都注入了依赖项。因此,类不负责创建自己的依赖项。这意味着它对依赖关系的具体实现一无所知。SomeClassThatNeedsMemberFactory
不知道具体的实现可能需要IRepository
。它只知道IMemberFactory
的接口。
这是依赖注入的重要组成部分。现在,如果我们更改IMemberFactory
的实现,它的使用者不需要知道它,他们不知道传递给它的构造函数的是什么。
它还使每个类更容易测试。如果您正在测试SomeClassThatNeedsMemberFactory
,则可以模拟IRespository
。
我还没有提到依赖注入/IoC容器,因为你没有问过它们,而且它们实际上不是依赖注入概念的核心。依赖项注入是关于构造类或方法,使它们接收依赖项,而不是创建它们——所有这些都是上面提到的。容器只是帮助我们管理所有这些。
假设我们使用的是IServiceCollection
/IServiceProvider
,这是目前.NET应用程序的默认容器。
如果我们在web应用程序的Startup
类中工作,或者在其他应用程序类型中使用类似的类,我们就可以做到这一点。
void ConfigureServices(IServiceCollection services)
{
services.AddScoped<SomeClassThatNeedsMemberFactory>();
services.AddScoped<IMemberFactory, MemberFactory>();
services.AddScoped<IRepository, SomeConcreteRepository>();
}
这告诉服务集合
- 我们将要求它创建
SomeClassThatNeedsMemberFactory
的实例 - 如果要创建任何需要
IMemberFactory
实例的内容,请创建MemberFactory
实例 - 如果它正在创建任何需要
IRepository
实例的内容,请使用SomeConcreteRepository
现在假设应用程序需要创建Web API控制器的实例,并且我们已经注入了该控制器的SomeClassThatNeedsMemberFactory
。服务提供商(依赖注入/IoC容器)会说
- 我需要创建一个
FooController
的实例。构造函数说它需要一个SomeClassThatNeedsMemberFactory
的实例,所以我将创建其中一个 - 等等,
SomeClassThatNeedsMemberFactory
的构造函数需要一个IMemberFactory
的实例。当我被要求IMemberFactory
时,我使用MemberFactory
,所以我将创建其中一个 - 等等,
MemberFactory
的构造函数需要一个IRepository
的实例。我将创建一个SomeConcreteRepository
的实例
只要我们告诉它要使用什么类型,它就可以创建复杂的对象,其中依赖项具有需要更多依赖项的依赖项,等等。但我们不必为此考虑太多。我们可以一次只看一个类,并在编写、处理或测试时注意它需要什么依赖关系
这就是为什么容器如此受欢迎。没有他们,我们可以做到这一切,但这更难。然而,它们并不是依赖注入的全部内容。他们只是启用它。