假设我们有
public interface ITimestampProvider
{
DateTime GetTimestamp();
}
和一个消耗它的类
public class Timestamped
{
private ITimestampProvider _timestampProvider
public Timestamped(ITimestampProvider timestampProvider)
{
// arg null check
_timestampProvider = timestampProvider;
}
public DateTime Timestamp { get; private set; }
public void Stamp()
{
this.Timestamp = _timestampProvider.GetTimestamp();
}
}
和默认实现:
public sealed class SystemTimestampProvider : ITimestampProvider
{
public DateTime GetTimestamp()
{
return DateTime.Now;
}
}
引入此构造函数是有用还是有害?
public Timestamped() : this(new SystemTimestampProvider())
{}
这是一个普遍的问题,即时间戳不是有趣的部分。
答案 0 :(得分:7)
我认为这取决于场景,并且基本上取决于代码所在的消费者(库与应用程序)以及您是否正在使用IoC容器。
如果您正在使用IoC容器,并且这不是公共API的一部分,那么让容器执行繁重的工作,并且只使用单个构造函数。添加no-args构造函数只会让事情变得混乱,因为你永远不会使用它。
如果这是公共API的一部分,那么请保留两者。如果您正在使用IoC,请确保您的IoC找到“最贪婪”的构造函数(具有最多参数的构造函数)。不使用IoC,但使用您的API的人将不会构建整个依赖图以便使用您的对象。
如果您没有使用IoC容器,但只想使用模拟进行单元测试,请保留no-args构造函数,并使内部的贪婪构造函数成为可能。为您的单元测试程序集添加InternalsVisibleTo,以便它可以使用贪婪的构造函数。如果您只是单元测试,那么您不需要额外的公共API表面。
答案 1 :(得分:4)
我不会提供那个构造函数。这样做可以很容易地调用新的TimeStamped,并在您的IoC配置为使用OtherTimestampProvider()时获取具有新SystemTimestampProvider()的实例。
一天结束时,你最终会花一点时间试图调试你为什么得到错误的时间戳。
如果您只提供第一个构造函数,则可以使用SystemTimestampProvider进行简单的查找,以找出(错误地)使用该提供程序而不是IoC配置的提供程序。
答案 2 :(得分:3)
总的来说,我不这么认为......这取决于你使用的依赖注入。当我使用DI进行单元测试时,我通过在注入的实例为null时实例化依赖对象的生产版本来做同样的事情(或多或少)...然后我有一个不带参数和委托的重载对于那个...我使用无参数的生产代码,并为单元测试方法注入一个测试版...
如果您正在谈论IOC容器应用程序,otoh,您需要注意干扰配置设置告诉容器以不明确的方式执行的操作......
public class EventsLogic
{
private readonly IEventDAL ievtDal;
public IEventDAL IEventDAL { get { return ievtDal; } }
public EventsLogic(): this(null) {}
public EventsLogic(IIEEWSDAL wsDal, IEventDAL evtDal)
{
ievtDal = evtDal ?? new EventDAL();
}
}
答案 3 :(得分:0)
我尽量避免这种情况 - 有几个地方我发现它是一个有用的设计,但更多时候我发现它只会导致我犯错误,这可能有点令人费解
通过使用依赖注入容器(我使用StructureMap)来管理所有这些连接,大大减少了对默认注入对象的需求 - DI容器确保您始终获得可以使用的具体实例。
我仍然想要使用你建议的构造函数的唯一地方是在我的单元测试中,但最近我使用假冒或模拟对象获得了更大的价值。
有些地方的默认依赖对象是正确而有用的设计,但总的来说,我会说你只是引入了不会增加很多价值的紧耦合。
答案 4 :(得分:0)
既没有帮助也没有害处。它带来了一个美学问题,因为只有当你的设计允许注册属性时,你才能将DI限制为构造函数注入。
另一种选择是实现一个返回默认实现的getter:
public DateTime Timestamp
{
get { return _timestampProvider??new SystemTimestampProvider(); }
set { _timestampProvider = value; }
}
或者,如果您担心在堆中创建太多对象,则可以使用单例实现上述内容。
答案 5 :(得分:0)
我的团队使用此方法取得了很大成功。我建议一个更改:
使_timestampProvider只读。这迫使提供者在构建时具有确定性并将消除错误。
public class Timestamped
{
private readonly ITimestampProvider _timestampProvider;
public Timestamped(ITimestampProvider timestampProvider)
{
_timestampProvider = timestampProvider;
}
public Timestamped(): this(new SystemTimestampProvider())
{ }
}
也就是说,我们始终正在研究新技术,包括DI框架。如果我们放弃这种技术以获得更好的效果,我会告诉你。