界面疯狂

时间:2009-03-31 12:50:51

标签: tdd inversion-of-control interface

我正在喝coolade并喜欢它 - 接口,IoC,DI,TDD等等。工作得很好。但我发现我必须努力使所有接口成为一种趋势!我有一个工厂,这是一个接口。它的方法返回可能是接口的对象(可能使测试更容易)。这些对象是他们所需服务的DI接口。我发现保持接口与实现同步是为了增加工作 - 向类添加方法意味着将它添加到类+接口,模拟等。

我是否过早地将接口分解出来?是否有最佳实践可以知道何时应该返回某个界面与对象?

8 个答案:

答案 0 :(得分:7)

当您想要模拟对象与其中一个协作者之间的交互时,接口非常有用。但是,对于具有内部状态的对象,接口的值较小。

例如,假设我有一个与存储库对话的服务,以便提取某些域对象,以便以某种方式对其进行操作。

从存储库中提取接口有明确的设计价值。我对存储库的具体实现很可能与NHibernate或ActiveRecord紧密相关。通过将我的服务链接到接口,我可以清楚地分离这个实现细节。事实上我也可以为我的服务编写超快速的独立单元测试,因为我可以将它交给模拟IRepository。

考虑从存储库返回的域对象以及我的服务所依赖的域对象,价值较低。当我为我的服务编写测试时,我会想要使用一个真实的域对象并检查它的状态。例如。在调用service.AddSomething()之后我想检查是否有东西被添加到域对象中。我可以通过简单检查域对象的状态来测试它。当我单独测试我的域对象时,我不需要接口,因为我只对对象执行操作并在其内部状态下对其进行测试。例如如果它正在睡觉,我的羊是否适合吃草?

在第一种情况下,我们对基于交互的测试感兴趣。接口有用,因为我们想拦截在测试对象和带有模拟的协作者之间传递的调用。在第二种情况下,我们对基于状态的测试感兴趣。接口在这里没有帮助。尝试意识到你是在测试状态还是交互,让它影响你的界面或没有界面决定。

请记住(如果您安装了Resharper的副本),以后提取界面非常便宜。如果您决定不需要该接口,那么删除接口并恢复到更简单的类层次结构也很便宜。我的建议是在没有接口的情况下启动,并在发现想要模拟交互时按需提取它们。

当你将IoC带入图片中时,我倾向于提取更多接口 - 但是试着控制你将多少个类移到你的IoC容器中。通常,您希望将这些限制为基本上无状态的服务对象。

答案 1 :(得分:6)

听起来你的BDUF还有点痛苦。

轻轻一点,让它自然流动。

答案 2 :(得分:6)

请记住,虽然灵活性是一个有价值的目标,但IoC和DI增加了灵活性(在某种程度上是TDD的要求)也增加了复杂性。唯一的灵活点是更快,更便宜或更好地进行下游变更。每个IoC / DI点都会增加复杂性,从而有助于在其他地方进行更改。

实际上,您需要一个大设计前端到某些范围:确定哪些区域最有可能发生变化(和/或需要进行大量的单元测试),并在那里规划灵活性。重构以消除不太可能发生变化的灵活性。

现在,我并不是说您可以猜测在任何准确度下都需要灵活性。你错了。但是你很可能会得到正确的答案。如果您以后发现不需要灵活性,可以在维护中考虑因素。在您需要的地方,可以在添加功能时考虑它。

现在,可能会或可能不会发生变化的区域取决于您的业务问题和IT环境。这是一些反复出现的地区。

  1. 我总是考虑外在的 您集成到的接口 其他系统是高度可变的。
  2. 无论什么代码提供后端 用户界面需要支持UI中的更改。但是,主要针对功能更改的计划:不要过分关注并规划不同的UI技术(例如支持智能客户端和Web应用程序 - 使用模式会有太大差异)。
  3. 另一方面,编码 对不同数据库的可移植性 和平台通常浪费 时间至少在企业中 环境。四处询问并检查 有什么计划可以取代或 升级技术 可能是您软件的使用寿命。
  4. 对数据内容和格式的更改非常棘手 业务:数据会 偶尔改变,大多数设计 我见过这样的变化 很糟糕,因此你得到了具体的 实体类直接使用。
  5. 但只有你可以判断什么可能或不应该改变。

答案 3 :(得分:5)

我经常发现我想要“服务”的接口 - 而主要关于“数据”的类型可以是具体的类。例如,我有一个Authenticator接口,但是Contact类。当然,并不总是那么明确,但这是一个初步的经验法则。

我确实感觉到你的痛苦 - 这有点像回到.h和.c文件的黑暗日子......

答案 4 :(得分:2)

我认为最重要的“敏捷”原则是YAGNI(“你不需要它”)。换句话说,在实际需要之前不要编写额外的代码,因为如果你事先编写代码,那么当你最终确实需要它时,需求和约束可能会发生变化。

接口,依赖注入等 - 所有这些都会增加代码的复杂性,使其更难理解和更改。我的经验法则是尽可能保持简单(但不简单)并且不增加复杂性,除非它让我获得足够的收益以抵消它所带来的负担。

因此,如果您实际上正在测试并且使用模拟对象非常有用,那么无论如何都要定义一个模拟和实类都实现的接口。但是,不要在纯粹假设的基础上创建一堆接口,它可能在某些时候有用,或者它是“正确的”OO设计。

答案 5 :(得分:0)

接口的目的是建立合同,当您想要更改动态执行任务的类时,接口尤其有用。如果不需要更改类,则接口可能会妨碍。

有自动提取界面的工具,所以也许你最好在这个过程的后期延迟界面提取。

答案 6 :(得分:0)

这在很大程度上取决于你提供的内容......如果你正在处理内部事情,那么“在需要之前不要做它们”的建议是合理的。但是,如果您正在制作一个供其他开发人员使用的API,那么以后将内容更改为接口可能会很烦人。

一个好的经验法则是从需要是子类的任何东西中创建接口。这不是“总是在这种情况下制作一个界面”的东西,你还需要考虑它。

因此,简短的答案是(并且这适用于内部事物并提供API),如果您预计将需要多个实现,那么将其设为接口。

通常不会是接口的某些东西会是只保存数据的类,比如说处理x和y的Location类。可能存在另一种实施方式的可能性很小。

答案 7 :(得分:-1)

不要先创建接口 - 你不需要它们。您无法猜测哪些类需要一个接口,而不是哪些类。因此,现在不要花费任何时间用无用的界面来加重代码。

但是当你感觉到这样做的冲动时 - 当你看到界面需要时 - 在重构步骤中提取它们。

Those answers也可以提供帮助。

相关问题