Oop 将接口对象传递给构造函数或方法

Oop 将接口对象传递给构造函数或方法,oop,testing,mocking,Oop,Testing,Mocking,我正在写一个测试,我必须通过一个模拟到一个特定的方法。我想知道,通过构造函数或直接将其传递到所讨论的方法中有什么好处吗。或者这并不重要 比如说 通过构造函数传递接口/mock class User() { IClock clock; User(IClock clock) {this.clock = clock;} User GetUser(){ ..} UpdateUser(User user) { ... this.clock.Now();

我正在写一个测试,我必须通过一个模拟到一个特定的方法。我想知道,通过构造函数或直接将其传递到所讨论的方法中有什么好处吗。或者这并不重要

比如说

通过构造函数传递接口/mock

class User()
{
  IClock clock;

  User(IClock clock) {this.clock = clock;}

  User GetUser(){ ..}
  UpdateUser(User user) {
     ... 
     this.clock.Now();
     ...
  }
}
vs

将接口/模拟传递到方法中

class User()
{
  User GetUser(){ ..}
  UpdateUser(IClock clock, User user) {
     ... 
     clock.Now();
     ...
  }
}
谢谢

编辑

在这种情况下,IClock将是包装DateTime。我这样做是为了可测试性。因此,有效地,我将有一个覆盖,它将在自身内部构造IClock

例如,对于方法案例:

UpdateUser(User user) {
  UpdateUser(new Clock(), user);
}

时钟将封装日期时间。

这取决于谁知道
IClock
:它在构建
用户
的地方可用,还是在调用
更新用户
的地方可用

如果将其传递给构造函数,则可以传递
User
对象,任何人都可以调用
UpdateUser
,但如果通过
UpdateUser
,则使用该方法的任何人都必须持有
IClock
引用。

将依赖项(即
IClock
)传递给构造函数,也就是说,构造函数注入通常意味着这是给定类的整个对象所必需的依赖关系。将依赖项传递给类的方法,即方法注入,只会使这个特定的方法依赖于它。还有另外两种可能性:将依赖项注入属性(属性注入,即类的对象的可选依赖项)和很少使用的环境上下文

所以,这更多的是关于团队中代码风格的品味、设计和一致性的问题


在您的示例中,
IClock
用户的对象不需要依赖项,因此方法注入更合适。

在这种情况下,IClock是一个隐藏的单例-它从底层操作系统生成当前日期时间。如果您想编写可靠的验收测试,您需要在这些测试中删除日期时间提供程序

因此,您需要将此依赖关系一直公开到模块边界,然后将公共日期时间源注入到您的模块(甚至整个系统)。在编写验收测试时,您有时会存根这个“IClock”日期时间源

这将允许您回答以下问题:

  • 我的系统在闰日工作吗
  • 我的系统在午夜工作吗
  • 我的系统在除夕有效吗
在构造函数签名或方法签名中具有IClock依赖项将向用户清楚地证明您的方法或对象依赖于日期和时间。在试图理解一个物体的行为时,对日期和时间的依赖通常是至关重要的,因此应予以突出。通常避免任何隐藏此依赖关系的操作(例如在重载方法定义中提供默认日期时间源),因为这将阻止您编写像我上面描述的那样的验收测试

在将依赖项传递给构造函数或方法之间进行选择通常取决于方法对系统其余部分的责任。这些方法形成了公共协议,对象将使用该协议与运行系统中的对等方进行通信,而构造函数参数是此特定实现执行其角色所需的依赖项

问自己以下问题:

  • 这种方法的责任是否一定需要知道日期和时间
  • 或者它与这个特定对象中方法的特定实现有关,这意味着它需要日期时间
  • 方法的调用方负责处理该对象的日期时间视图是否合理,或者这是模块配置的责任
  • 如果我将此对象与同一接口的另一个实现交换,那么该实现不需要此方法的日期时间是否合理
  • 对象中有多少方法需要日期时间?他们需要相同的日期时间视图吗

实际上,在这种情况下,IClock将是包装日期时间。牛顿:在这种情况下,我宁愿把它放在构造函数中,因为它减少了类中公共方法的数量。@casablanca我认为你提出了一个非常好的观点:“如果你通过UpdateUser传递它,任何使用该方法的人都必须持有IClock引用”。