带工厂的依赖注入用于带构造函数参数的子类

本文关键字:子类 参数 构造函数 注入 工厂 依赖 用于 | 更新日期: 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个。