我有这个使用Ninject for 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优点,比如生命周期管理,或者将一个实现换成另一个。有更优雅的方式吗?
答案 0 :(得分:0)
而不是做
new SomeService(new Repo(id));
你可以做到
IResolutionRoot.Get<Repo>(new ConstructorArgument("id", id));
IResolutionRoot
是内核的类型解析接口,您可以注入构造函数。这将使您从“DI善良”中受益,正如您所说的那样。请注意,您可能还想使用ninject.extensions.contextpreservation
扩展程序。
ninject.extensions.factory
,可以帮助你消除锅炉板代码,基本上完全按照我的描述进行操作。
通过将IResolutionRoot.Get<>
调用移动到另一个类(.. factory ...),您可以从实例中解除SomeService
的负担。 ninject.extension.factory
正是这样做的。由于扩展不需要实际实现,您甚至可以节省一些单元测试!那么,如果SomeService被扩展并且需要更多的依赖关系,那么DI管理呢?好吧不用担心,因为你使用内核来实现类,它会自动工作,IoC将按照它应该管理你的依赖项。
如果您不使用DI进行实例化,最终可能会在最后使用非常少的DI。
正如@ zafeiris.m所指出的,这称为服务定位器。并且通常也被称为反模式。这是正确的!只要你能够获得更好的解决方案,你应该。在这里你可能不会。可以说它更好,所以坚持使用最好的解决方案,无论人们怎么称呼它。
可能有办法减少服务地点的使用。例如,如果您有一个业务案例,如“UpdateOrder(id)”,该方法将为该ID创建一个服务,然后为该ID创建另一个服务,然后为该ID创建一个记录器,您可能想要更改它创建一个对象,该对象将ID作为可继承的构造函数参数,并将特定于ID的服务和记录器注入该对象,从而将3个工厂/服务定位器调用减少为1.
答案 1 :(得分:0)
按照你的描述保持工厂,对我来说似乎很好。这就是它们的用途,当您拥有只能在运行时根据某些运行时值实例化的服务时(例如您的id
),工厂就可以了。
但如果我这样做,我会失去所有的DI善良,就像生命周期一样 管理,或将一个实施与另一个实施交换。
DI不是工厂的替代品。您需要两个世界,这将导致灵活,松散耦合的设计。工厂用于新建依赖关系,他们知道如何创建它们,使用什么配置以及何时处理它们,如果你想交换实现,你仍然只改变一个地方:只是这次它是工厂,而不是你的DI配置。
作为最后一点,你可能会试图使用另一个答案中提出的service locator anti-pattern。您最终会得到更糟糕的设计,更少可测试,更不清晰,可能与您的DI容器紧密耦合等等。您使用工厂的想法要好得多。 (here are更多示例,与您的类似。)