我刚刚在 PluralSight 上观看了一个依赖注入课程,它为我解决了一些问题。但是,尽管它跨越了几层,但也没有关于当你只需要在你的 codede 中更新对象时究竟要走什么路的信息。
这是我努力掌握的最后一块拼图。我知道 Compisition Root 以及大致如何使用它。我知道要避免使用 ServiceLocator 并通过将 IoC 容器定义为代码库中几乎每个类中的 ctor 参数来避免将 IoC 容器传递到代码的所有级别(我都曾在我工作的公司中并排看到过)为了)。
让我们考虑下面的两个例子,我认为把所有东西都交出来是不切实际的。
我可以注入依赖项,但我不可能注入所有东西,毕竟它不称为对象注入,所以问题是如何在循环内动态创建 Doee 对象:
public class MyClass
{
private IVerifyer _verifyer;
public MyClass(IVerifyer verifyer)
{
_verifyer = verifyer;
}
public IList<Doer> PrepareDoingSomething(IList<IDoees> doees)
{
var doers = new List<Doer>();
foreach (var doee in doees)
{
if (!Verifyer.Verify(doee)) throw new Exception("Blablabla...");
doers.Add(new Doer(doee));
}
return doers;
}
}
现在在一些“不太动态”的情况下,问题是对于一些 IO 类既没有接口也没有抽象基类可用,这意味着它们很难在测试代码中处理,而且我什至不知道如何使用 IoC 容器处理它们,例如 Process 类:
public class Processor
{
public Process ProcessSomething(IProcessee processee)
{
// do some pre-processing stuff here
// static Start() returns a new Process instance
return Process.Start("C:\MyApp.exe", $"-option {processee.Option1}");
}
}
到目前为止,我所做的是在这两种情况下都引入一个我可以注入的抽象工厂类,并在需要时为我提供所需的东西(即在循环中动态地)。我有一个生产使用的实现。在测试代码中,我可以实现它或者只是模拟它(因为它是抽象的)。对于 Process 类,我引入了一个带有接口 (I)ProcessProxy 的代理类,它通过对实际 Process 类的调用传递。如果需要,我也可以轻松地嘲笑它。然后这两个示例变成了我在下面列出的内容。
我的问题是,在这两种情况下(这是我主要关心的问题),这是正确的方法吗?我知道我可能会引发固执己见的答案,但我只是想弄清楚这是否是本着干净直接的教科书(如 DependencyInjection 和 CompositionRoot 实现)的精神推荐的方式。如果这不是首选的方式,那么它是什么?
重构和启用 DI 后的循环示例:
public class MyClass
{
private IVerifyer _verifyer;
private AbstractDoerFactory _doerFactory;
public MyClass(IVerifyer verifyer, AbstractDoerFactory doerFactory)
{
_verifyer = verifyer;
_doerFactory = doerFactory;
}
public IList<Doer> PrepareDoingSomething(IList<IDoees> doees)
{
var doers = new List<Doer>();
foreach (var doee in doees)
{
if (!_verifyer.Verify(doee)) throw new Exception("Blablabla...");
doers.Add(_doerFactory.GetNewDoer(doee));
}
return doers;
}
}
重构并启用 DI 后的流程示例:
public interface IProcessProxy : IDisposable
{
TextReader StandardOutput { get; }
TextReader StandardError { get; }
int ExitCode { get; }
Start(string fileName, string arguments);
void Kill();
}
public class Processor
{
private AbstractProcessProxyFactory _processProxyFactory;
public Processor(AbstractProcessProxyFactory processProxyFactory)
{
_processProxyFactory = processProxyFactory;
}
public IProcessProxy ProcessSomething(IProcessee processee)
{
// do some pre-processing stuff here
var processProxy = _processProxyFactory.GetProxyFactory();
return processProxy.Start("C:\MyApp.exe", $"-option {processee.Option1}");
}
}