我正在喝coolade并喜欢它 - 接口,IoC,DI,TDD等等。工作得很好。但我发现我必须努力使所有接口成为一种趋势!我有一个工厂,这是一个接口。它的方法返回可能是接口的对象(可能使测试更容易)。这些对象是他们所需服务的DI接口。我发现保持接口与实现同步是为了增加工作 - 向类添加方法意味着将它添加到类+接口,模拟等。
我是否过早地将接口分解出来?是否有最佳实践可以知道何时应该返回某个界面与对象?
答案 0 :(得分:7)
当您想要模拟对象与其中一个协作者之间的交互时,接口非常有用。但是,对于具有内部状态的对象,接口的值较小。
例如,假设我有一个与存储库对话的服务,以便提取某些域对象,以便以某种方式对其进行操作。
从存储库中提取接口有明确的设计价值。我对存储库的具体实现很可能与NHibernate或ActiveRecord紧密相关。通过将我的服务链接到接口,我可以清楚地分离这个实现细节。事实上我也可以为我的服务编写超快速的独立单元测试,因为我可以将它交给模拟IRepository。
考虑从存储库返回的域对象以及我的服务所依赖的域对象,价值较低。当我为我的服务编写测试时,我会想要使用一个真实的域对象并检查它的状态。例如。在调用service.AddSomething()之后我想检查是否有东西被添加到域对象中。我可以通过简单检查域对象的状态来测试它。当我单独测试我的域对象时,我不需要接口,因为我只对对象执行操作并在其内部状态下对其进行测试。例如如果它正在睡觉,我的羊是否适合吃草?
在第一种情况下,我们对基于交互的测试感兴趣。接口有用,因为我们想拦截在测试对象和带有模拟的协作者之间传递的调用。在第二种情况下,我们对基于状态的测试感兴趣。接口在这里没有帮助。尝试意识到你是在测试状态还是交互,让它影响你的界面或没有界面决定。
请记住(如果您安装了Resharper的副本),以后提取界面非常便宜。如果您决定不需要该接口,那么删除接口并恢复到更简单的类层次结构也很便宜。我的建议是在没有接口的情况下启动,并在发现想要模拟交互时按需提取它们。
当你将IoC带入图片中时,我倾向于提取更多接口 - 但是试着控制你将多少个类移到你的IoC容器中。通常,您希望将这些限制为基本上无状态的服务对象。
答案 1 :(得分:6)
听起来你的BDUF还有点痛苦。
轻轻一点,让它自然流动。
答案 2 :(得分:6)
请记住,虽然灵活性是一个有价值的目标,但IoC和DI增加了灵活性(在某种程度上是TDD的要求)也增加了复杂性。唯一的灵活点是更快,更便宜或更好地进行下游变更。每个IoC / DI点都会增加复杂性,从而有助于在其他地方进行更改。
实际上,您需要一个大设计前端到某些范围:确定哪些区域最有可能发生变化(和/或需要进行大量的单元测试),并在那里规划灵活性。重构以消除不太可能发生变化的灵活性。
现在,我并不是说您可以猜测在任何准确度下都需要灵活性。你错了。但是你很可能会得到正确的答案。如果您以后发现不需要灵活性,可以在维护中考虑因素。在您需要的地方,可以在添加功能时考虑它。
现在,可能会或可能不会发生变化的区域取决于您的业务问题和IT环境。这是一些反复出现的地区。
但只有你可以判断什么可能或不应该改变。
答案 3 :(得分:5)
我经常发现我想要“服务”的接口 - 而主要关于“数据”的类型可以是具体的类。例如,我有一个Authenticator
接口,但是Contact
类。当然,并不总是那么明确,但这是一个初步的经验法则。
我确实感觉到你的痛苦 - 这有点像回到.h和.c文件的黑暗日子......
答案 4 :(得分:2)
我认为最重要的“敏捷”原则是YAGNI(“你不需要它”)。换句话说,在实际需要之前不要编写额外的代码,因为如果你事先编写代码,那么当你最终确实需要它时,需求和约束可能会发生变化。
接口,依赖注入等 - 所有这些都会增加代码的复杂性,使其更难理解和更改。我的经验法则是尽可能保持简单(但不简单)并且不增加复杂性,除非它让我获得足够的收益以抵消它所带来的负担。
因此,如果您实际上正在测试并且使用模拟对象非常有用,那么无论如何都要定义一个模拟和实类都实现的接口。但是,不要在纯粹假设的基础上创建一堆接口,它可能在某些时候有用,或者它是“正确的”OO设计。
答案 5 :(得分:0)
接口的目的是建立合同,当您想要更改动态执行任务的类时,接口尤其有用。如果不需要更改类,则接口可能会妨碍。
有自动提取界面的工具,所以也许你最好在这个过程的后期延迟界面提取。
答案 6 :(得分:0)
这在很大程度上取决于你提供的内容......如果你正在处理内部事情,那么“在需要之前不要做它们”的建议是合理的。但是,如果您正在制作一个供其他开发人员使用的API,那么以后将内容更改为接口可能会很烦人。
一个好的经验法则是从需要是子类的任何东西中创建接口。这不是“总是在这种情况下制作一个界面”的东西,你还需要考虑它。
因此,简短的答案是(并且这适用于内部事物并提供API),如果您预计将需要多个实现,那么将其设为接口。
通常不会是接口的某些东西会是只保存数据的类,比如说处理x和y的Location类。可能存在另一种实施方式的可能性很小。
答案 7 :(得分:-1)
不要先创建接口 - 你不需要它们。您无法猜测哪些类需要一个接口,而不是哪些类。因此,现在不要花费任何时间用无用的界面来加重代码。
但是当你感觉到这样做的冲动时 - 当你看到界面需要时 - 在重构步骤中提取它们。
Those answers也可以提供帮助。