[英]Simple Injector:Factory classes that need to create classes with dependencies
我有一个工厂类,可以创建几种不同类型的类。 工厂已在集装箱中注册。 在工厂内部创建类的建议方法是什么,因为它们也有依赖项。 我显然希望避免对容器的依赖,但如果我新建这些类,那么他们将不会使用容器。 例如
public class MyFactory
{
public IMyWorker CreateInstance(WorkerType workerType)
{
if (workerType == WorkerType.A)
return new WorkerA(dependency1, dependency2);
return new WorkerB(dependency1);
}
}
所以问题是我从哪里获取这些依赖项。
一种选择可能是使它们成为工厂的依赖关系。 例如
public class MyFactory
{
private Dependency1 dependency1;
private Dependency2 dependency2;
public MyFactory(Dependency1 dependency1, Dependency2, dependency2)
{
this.dependency1 = dependency1; this.dependency2 = dependency2;
}
public IMyWorker CreateInstance(WorkerType workerType)
{
if (workerType == WorkerType.A)
return new WorkerA(dependency1, dependency2);
return new WorkerB(dependency1);
}
}
另一个可能是注册工人类型并制造工厂的依赖关系,例如
public class MyFactory
{
private IWorkerA workerA;
private IWorkerB workerB;
public MyFactory(IWorkerA workerA, IWorkerB, workerB)
{
this.workerA = workerA; this.workerB = workerB;
}
public IMyWorker CreateInstance(WorkerType workerType)
{
if (workerType == WorkerType.A)
return workerA;
return workerB;
}
}
有了第一个选项,我觉得我正在把工人的依赖性带到工厂里。 使用第二个选项,在创建工厂时创建工作程序。
思考?
我同意@Phil,让工厂依赖集装箱是好的,但他的答案中缺少一个信息和平。
您可能试图阻止对容器的依赖,因为您试图远离Service Locator反模式 。 我同意Service Locator是一种反模式,应该被阻止。
对容器的依赖是否是Service Locator反模式的实现取决于此消费者的定义位置。 Mark Seemann 在此解释了这一点 :
封装在Composition Root中的DI容器不是服务定位器 - 它是基础架构组件。
因此,只要在 组合根目录中定义此MyFactory
实现,让工厂依赖容器就可以了。
当你这样做时,你很快就会遇到麻烦,因为组合根中定义的类不能从应用程序的其余部分引用。 但是,通过在应用程序中定义IMyFactory
接口并让工厂实现实现该接口(正如您应该遵循依赖性反转原则那样 ),可以轻松解决该问题。
所以你的注册会变成这样:
container.RegisterSingleton<IMyFactory, MyFactory>();
和这样的实现:
private sealed class MyFactory : IMyFactory
{
private readonly Container container;
public MyFactory(Container container)
{
this.container = container;
}
public IMyWorker CreateInstance(WorkerType workerType)
{
if (workerType == WorkerType.A)
return this.container.GetInstance<IWorkerA>();
return this.container.GetInstance<IWorkerB>();
}
}
这是一个经典问题。
如果不同实现的数量增加,那么您的两个解决方案都可能会出现问题,特别是如果每个实现的依赖关系有很多差异。 你最终可能会得到一个带20个参数的构造函数。
我首选的实现是让工厂类引用容器,并以这种方式解析所需的实例。
有些人可能认为这并不比Service Locator反模式好,但我觉得这个问题没有一个完美的解决方案,这样做对我来说似乎是最自然的。
在我看来,解决方案在很大程度上取决于两个依赖项的生命周期。 这些依赖项是否可以跨对象共享,然后您可以在容器中注册它们并将它们传递给工厂并重用它们。 如果工厂的每个“产品”都应该拥有它自己的实例,那么也许您可以考虑为这些依赖项构建一个单独的工厂(当然,如果它们属于相同的“系列”对象)并将其传递给您的工厂,并询问对于每当您创建IMyWorker实例时的实例。 或者,在创建最终产品之前,您可以考虑使用Builder而不是Factory来创建每个依赖项 - 在您的案例中使用IMyWorker。
传递容器被认为是代码气味,除非您正在实现组合根,例如在WCF中。
如果您最终在构造函数中使用了许多依赖项的工厂,那么您应该将其视为提示,这是错误的 - 最有可能的是违反单一责任原则 - 它只是知道太多;)
一本关于依赖注入的非常好的书是由Mark Seemann撰写的“.NET中的依赖注入”一书,我推荐:)
虽然这个问题是主观的(答案也是如此),但我会说你的第一种方法是合适的。
使用Dependency Injection
您必须了解什么是实际依赖项。 在这种情况下, WorkerA
和WorkerB
不是真正的依赖项,但显然Dependency1
和Dependency2
是依赖项。 在现实世界中,我在Micrsoft Prism应用程序中使用了这种模式。
希望我的应用程序的一个示例将使您更好地理解要使用的模式。 我使用了ILoggerFacade
依赖项。 我有一些视图模型,它们位于一个单独的组件中(工厂也驻留在该组件中)。 我个人的IPlayerViewModel
不是依赖项(这就是为什么我没有去第二条路线)。
ShellViewModel.cs:
[Export]
public sealed class ShellViewModel : NotificationObject
{
public ShellViewModel()
{
Players = new ObservableCollection<IPlayerViewModel>();
// Get the list of player models
// from the database (ICollection<IPlayer>)
var players = GetCollectionOfPlayerModels();
foreach (var player in players)
{
var vm = PlayerViewModelFactory.Create(player);
Players.Add(vm);
}
}
[Import]
private IPlayerViewModelFactory PlayerViewModelFactory { get; set; }
public ObservableCollection<IPlayerViewModel> Players { get; private set; }
}
IPlayerViewModelFactory.cs
public interface IPlayerViewModelFactory
{
IPlayerViewModel Create(IPlayer player);
}
IPlayer.cs
public interface IPlayer
{
// Sport Enum
Sport Sport { get; set; }
}
单独的程序集/ PlayerViewModelFactory.cs
[Export]
public sealed class PlayerViewModelFactory : IPlayerViewModelFactory
{
[Import]
private ILoggerFacade Logger { get; set; }
public IPlayerViewModel Create(IPlayer player)
{
switch (player.Sport)
{
case Sport.Basketball:
return new BasketballViewModel(Logger, player);
case Sport.Football:
return new FootballViewModel(Logger, player);
// etc...
default:
throw new ArgumentOutOfRangeException("player");
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.