带工厂的依赖注入用于带构造函数参数的子类
本文关键字:子类 参数 构造函数 注入 工厂 依赖 用于 | 更新日期: 2023-09-27 18:02:28
我有这个应用程序,使用Ninject为DI,我有以下结构:
public class SomeServicHost
{
private ISomeService service;
public SomeServicHost(ISomeService service)
{
this.service = service;
}
public void DoSomething(int id)
{
// Injected instance
this.service.DoWhatever("Whatever");
// Without injection - this would be how it would be instantiated
var s = new SomeService(new Repo(id));
s.DoWhatever("Whatever");
}
}
interface ISomeService
{
void DoWhatever(string id);
}
class SomeService : ISomeService
{
private IRepo SomeRepo;
public SomeService(IRepo someRepo)
{
this.SomeRepo = someRepo;
}
public void DoWhatever(string id)
{
SomeRepo.DoSomething();
}
}
interface IRepo
{
void DoSomething();
}
class Repo : IRepo
{
private int queueId;
public Repo(int queueId)
{
this.queueId = queueId;
}
public void DoSomething()
{
// Whatever happens
}
所以正常注射不起作用。我需要一个工厂。我可以这样写:
interface IServiceFactory
{
ISomeService GetService(int id)
{
return new SomeService(new Repo(id));
}
}
实际上我已经有了。但是如果我这样做,我就会失去所有的DI优点,比如生命周期管理,或者用另一个实现替换一个实现。有没有更优雅的方法?
按你所说的保留工厂,我觉得没问题。这就是它们的作用,当你的服务只能在运行时实例化,这取决于一些运行时值(比如你的id
),工厂是一种方式。
但是如果我这样做,我就失去了所有DI的优点,比如生命周期管理,或者用另一个实现替换一个实现。
DI不是工厂的替代品。您需要这两个世界来实现灵活、松散耦合的设计。工厂是用于新建依赖的,它们知道如何创建依赖,使用什么配置以及何时处理它们,如果你想要交换实现,你仍然只需要改变一个地方:只是这次是工厂,而不是你的DI配置。
最后要注意的是,您可能想使用服务定位器反模式,正如另一个答案中所建议的那样。你只会得到更糟糕的设计,更难以测试,更不清晰,可能与你的DI容器紧密耦合,等等。你用工厂的主意好得多。(这里有更多类似的例子)不做
new SomeService(new Repo(id));
你可以做
IResolutionRoot.Get<Repo>(new ConstructorArgument("id", id));
IResolutionRoot
是内核的类型解析接口,您可以为它注入构造函数。这将使您受益于您所说的"DI善良"。请注意,您可能还想使用ninject.extensions.contextpreservation
扩展名。
ninject.extensions.factory
, @Simon Whitehead已经指出,可以帮助您消除锅炉板代码,基本上就是我上面描述的。
通过将IResolutionRoot.Get<>
调用移动到另一个类(…)你从实例化中解除SomeService
的负担。ninject.extension.factory
正是这样做的。由于扩展不需要实际实现,您甚至可以为自己节省一些单元测试!此外,如果扩展了SomeService并需要更多依赖项(由DI管理),该怎么办?好吧,不用担心,因为您使用内核实例化类,它将自动工作,IoC将管理您的依赖关系。如果您不使用DI来进行实例化,那么您最终可能会使用很少的DI。
@zafeiris。我指出这叫做服务定位器。并且通常也被称为反模式。确实如此!只要你能找到更好的解决方案,你就应该去做。在这里你可能做不到。我只想说,这是最好的,所以坚持最好的解决方案,不管别人怎么说。
可能有办法减少服务位置的使用。例如,如果您有一个业务案例,如"UpdateOrder(id)",该方法将为该id创建一个服务,然后为该id创建另一个服务,然后为该id创建一个记录器,…您可能希望将其更改为创建一个对象,该对象将ID作为可继承的构造函数参数,并将特定于ID的服务和记录器注入该对象,从而将3个工厂/服务定位器调用减少到1个。