假设我的生产代码是这样开始的:
public class SmtpSender
{
....
}
public void DoCoolThing()
{
var smtpSender = new SmtpSender("mail.blah.com"....);
smtpSender.Send(...)
}我有一个脑电波,决定用一个假的SmtpSender和依赖注入来测试这个函数:
public interface ISmtpSender
{
...
}
public class SmtpSender : ISmtpSender
{
...
}
public class FakeSmtpSender : ISmtpSender
{
...
}
public void DoCoolThing(ISmtpSender smtpSender)
{
smtpSender.Send(...)
}然后我想等等,我所有的生产代码都想要真正的代码。那么,为什么不让参数是可选的,如果不提供的话,用默认的SmtpSender填充它呢?
public void DoCallThing(ISmtpSender smtpSender = null)
{
smtpSender = smtpSender ?? new SmtpSender("...");
smtpSender.Send(...);
}这允许单元测试伪造它,同时保持生产代码不受影响。
这是依赖注入的可接受的实现吗?
如果没有,有哪些缺陷?
发布于 2014-05-02 01:30:01
不,调用者依赖于依赖关系的具体实现,这并不是进行依赖注入的最佳方法。组件实现应该始终只依赖于组件接口,而不是依赖于其他组件实现。
如果一个实现依赖于另一个实现,
我将通过将实现的产品选择放在代码或配置文件中或任何指定所有实现的内容中来解决您想要解决的问题。
发布于 2014-05-02 02:28:03
这是一种有效的使用方法,但需要严格限制在某些情况下使用。有几个陷阱值得警惕。
优点:
缺点:
通常,您应该只为非常轻量级的缺省值保留一些内容(比如空对象)。否则,事情就很容易失控。
您要做的类似于使用默认构造函数,以便使事情变得更方便。你应该确定这种便利是否会带来长期的好处,或者它最终是否会再次困扰你。
尽管您没有使用默认的构造函数,但我认为您应该考虑这些相似之处。Mark建议默认构造函数可能是设计上的味道:(强调我的)
默认构造函数是代码气味。给你拿着。这听起来可能有些离谱,但请考虑这一点:面向对象是将行为和数据封装到代码(类)的内聚块中。封装意味着类应该保护它封装的数据的完整性。当需要数据时,它通常必须通过构造函数提供。相反,--默认构造函数--意味着不需要外部数据。这是一个关于类的不变量的弱语句.
考虑到所有这些,我认为您提供的示例将是一种危险的方法,除非默认SMTP发送方是Null对象(例如:实际上没有发送任何内容)。否则,有太多的信息隐藏在复合根,这只是打开大门,令人不快的惊喜。
https://stackoverflow.com/questions/23419034
复制相似问题