让我们举一个简单的例子-也许您正在注入一种记录方式。
注入课程
class Worker: IWorker
{
ILogger _logger;
Worker(ILogger logger)
{
_logger = logger;
}
void SomeMethod()
{
_logger.Debug("This is a debug log statement.");
}
}
我认为这很清楚。而且,如果您使用的是IoC容器,则无需显式注入任何东西,只需将其添加到合成根目录中:
container.RegisterType<ILogger, ConcreteLogger>();
container.RegisterType<IWorker, Worker>();
....
var worker = container.Resolve<IWorker>();
调试时Worker
,开发人员只需要查阅组合根目录即可确定正在使用的具体类。
如果开发人员需要更复杂的逻辑,则可以使用整个界面:
void SomeMethod()
{
if (_logger.IsDebugEnabled) {
_logger.Debug("This is a debug log statement.");
}
}
注入方法
class Worker
{
Action<string> _methodThatLogs;
Worker(Action<string> methodThatLogs)
{
_methodThatLogs = methodThatLogs;
}
void SomeMethod()
{
_methodThatLogs("This is a logging statement");
}
}
首先,请注意,构造函数参数的名称现在更长methodThatLogs
。这是必要的,因为您无法确定an Action<string>
应该做什么。使用该接口,它是完全清楚的,但是在这里,我们必须依靠参数命名。这似乎天生就不太可靠,并且在构建期间很难执行。
现在,我们如何注入这种方法?好吧,IoC容器不会为您做这件事。因此,您在实例化时将其明确注入Worker
。这带来了两个问题:
- 实例化一个更多的工作
Worker
- 尝试进行调试的开发人员
Worker
会发现,很难弄清楚具体实例被调用了。他们不能仅仅参考合成词根。他们将不得不跟踪代码。
如果我们需要更复杂的逻辑怎么办?您的技术仅公开一种方法。现在,我想您可以将复杂的内容烘焙到lambda中:
var worker = new Worker((s) => { if (log.IsDebugEnabled) log.Debug(s) } );
但是在编写单元测试时,如何测试该lambda表达式?它是匿名的,因此您的单元测试框架无法直接实例化它。也许您可以找出一些聪明的方法来做到这一点,但是它可能比使用接口更大的PITA。
差异摘要:
- 仅注入方法会使推断目标变得更加困难,而接口则清楚地传达了目标。
- 仅注入方法会使接收注入的类的功能较少。即使您今天不需要,明天也可能需要。
- 您不能仅使用IoC容器自动注入方法。
- 您无法从组合根目录中判断在特定实例中哪个具体的类在起作用。
- 对lambda表达式本身进行单元测试是一个问题。
如果您对上述所有方法都满意,则可以只注入该方法。否则,我建议您坚持传统并注入一个界面。