使每个具体类继承接口是错误的吗?

时间:2010-02-18 02:54:31

标签: java spring oop naming-conventions guice

这是对很久以前Zed Shaw在博客中提出的某些comments的回应。

  

然后专家们会闲逛   实施他们的巴别塔火焰塔   没有任何评论,非常复杂   模拟启用测试,确保每一个   单一类有一个接口,和   用“Impl”结束每个班级   因为,那是最好的   实践。

我同样使用Spring和Google Guice,我注意到这些框架确实使用了 Impl 后缀,但是很少。在我的代码中,我到处使用接口,因为我被告知它使模拟等更容易。我对这个问题有一个天真的理解吗? (也许模拟框架可以使用抽象类或类,我不知道。我从未尝试过)对于我的具体实现,我选择了Spring的约定,在实现的名称前加上单词Default。

e.g. BottleOpener (interface) is implemented by DefaultBottleOpener (class)

这个问题的最佳做法是什么?

UPDATE1 我发现从方法返回接口很有用,因为我总是可以返回一个匿名类。

13 个答案:

答案 0 :(得分:17)

这不仅仅是嘲笑。在Spring的情况下,它是关于动态代理和面向方面的编程。这就是Spring如何处理所有类型的事务,远程处理和方面。

我将接口用于存储库,服务等,但我没有将接口放在模型对象或其他任何实现不太可能改变的地方。

将API与它们的实现方式分开。如果您的实现没有改变,那么界面没有多大意义。

Zed Shaw是一位了不起的作家和开发者,但他带着他的话说得很开心。我认为他沉迷的一些夸张的夸张是娱乐价值。他有一个观点(“不要做事只是因为有人自称是一个权威人士告诉你这是'最佳实践'”),但他所说的方式是部分剧院。

答案 1 :(得分:6)

最佳做法是:

  • 选择一个约定并保持一致;和
  • 不要过分使一切都实现接口。

答案 2 :(得分:6)

本书Interface Oriented Design有一种思考接口的有用方式 - 它们属于客户端,而不是提供者。因此,不考虑给定类是否应该由接口表示,而是考虑给定类可能要与之交互的接口,然后查找或编写符合这些接口的类。与实现类中较长的公共特性列表相比,接口可能非常简化 - 当然,给定的类可以实现多个接口。寻找类和接口之间的一对一对应关系,甚至将接口视为完整类的表示,并不是一种考虑接口的方法。

答案 3 :(得分:2)

这在很大程度上是一个主观问题。最好的响应可能是由于您可以尽可能多地对代码进行单元测试,但不要让界面创建妨碍完成任务。如果您的代码很快被淹没,请将其修剪下来。

那就是说,我已经看到代码中有400个类和400多个接口用于这些类。只是通过其糟糕的命名约定来挖掘一堆代码就足以让我有所了解。另一方面,我正在处理没有接口的第三方专有库。嘲弄这些物体会变得很痛苦。

答案 4 :(得分:2)

我认为为每个类创建一个接口没有任何好处。这只会迫使你两次编写一堆代码。

我认为不需要创建一个接口来区分API和实现。具有签名的类的公共函数列表是API。如果该类需要不属于API的worker-bee函数,则它们不应该是公共的。我没有看到

我在给定的上下文中创建一个接口,它有用的目的。基本上这意味着:

(a)在实践中,我最常见的原因是实现/模拟多重继承。如果我需要A扩展B但我还需要一个接受C的函数,我想将A传递给该函数,那么我必须使B或C成为一个接口而不是父类并使它成为一个扩展B实现C,反之亦然。

(b)当我为库或实用程序函数创建一个API时,它将由该库或实用程序之外的东西实现,通常由多个人来实现。 Java库中的“Comparable”接口就是一个例子。排序实用程序需要一个函数来比较一对对象。当程序使用调用程序中定义的对象调用sort实用程序时,不能指望sort实用程序知道如何比较它们,因此调用程序必须提供compare函数。比较函数的签名在逻辑上位于调用者可以为其对象实现的接口中。

(c)如果我想发布一个接口而不透露实现。这可能是出于专有原因,但实际上通常是因为其他人或团队将实施该界面。

答案 5 :(得分:2)

任何为另一个协作者提供“服务”的类都应该有一个接口 - 您可能希望切断并更改实现以实现所需的行为。例如,您可以轻松地用电子邮件服务替换Twitter服务。

任何代表'value'对象的类都不需要接口,因为它只服务于一个目的。在测试中,您甚至不需要模拟这些对象,只需在它们定义对象图的叶节点时直接使用它们。

答案 6 :(得分:1)

我听说'在SmallTalk中,你总是在实际实现之前开始定义接口......所以我想,这实际上取决于你想要实现的目的和设计目标......

答案 7 :(得分:1)

在对象上拥有接口,并且只允许对象通过接口相互通信,这是一件好事。

请注意,我的意思是对象,而不是集合或其他本质上是“数据”的东西。

但是,具有类实现'IClassName'或类似的类,并且在整个代码库中具有该模式反映了对接口概念的不良理解。接口通常应该由消费类声明,作为'嘿,这是我需要的服务'的声明。这样,界面表示两个对象之间的交互,而不是对象与世界的单个视图。它还有助于将职责分开,因为每个类都处理自己的理想接口,因此可以帮助保持域逻辑与实现逻辑分离。

(编辑)

这实际上是关于间接和抽象之间的区别。直接匹配API的接口就是间接接口。直接匹配使用者需求的接口是一种抽象,因为它声明消费者想要完成的 ,并隐藏 的所有信息。

Zed Shaw有一篇很好的文章。

http://www.zedshaw.com/essays/indirection_is_not_abstraction.html

答案 8 :(得分:1)

我不认为你可以通过隐藏接口背后的所有具体类来解决问题。

有些情况下它是多余的,但它不会花费太多。

甚至可以使域对象实现接口。优点:

  • 你可以隐藏丑陋的细节 客户代码(例如JPA 注释,将被使用的setter 仅限ORM框架)
  • 就像DAO或服务一样 你可以编程 抽象。想想用户 抽象 - 您的业务逻辑 不依赖于方式和地点 它们被存储(例如DB或OS), 并且您可以轻松使用适配器 如果你必须使用模式 第三方代码。

答案 9 :(得分:1)

我认为这取决于你的设计。如果您想使用依赖注入或模拟对象测试,那么接口是有意义的。此外,如果您计划进行多个实现,那么肯定需要接口来声明行为的合同。

如果有疑问,或许采取敏捷方法?

只需要一个啤酒开瓶器

class BeerBottleOpener // life's good

现在需要一个开瓶器

class WineBottleOpener // life's still good

重构一点

interface BottleOpener;
class BeerBottleOpener implements BottleOpener;
class WineBottleOpener implements BottleOpener;
// now living the life!  ;-)

顺便说一句,对于模拟测试,我使用了需要接口的EasyMock。现在切换到Mockito,它可以模拟类,更明确,更简单。

答案 10 :(得分:0)

在接口上过分是非常现实的可能性。忽略使用虚拟调度进行每个方法调用的[可忽略]性能损失,它还会在系统中引入更多选项。它实际上可以使调试和保护软件变得更加困难。如果您的方法/函数接受任何旧的接口实现者,则您已决定接受实现者可能已经错误地或者甚至恶意地实现了接口。您的程序/库/框架如何允许变化是设计过程的重要部分。

答案 11 :(得分:0)

为一切界面(或更一般地设计可能未使用的灵活性)提供所谓的过度工程。如果你可能永远不会有多个具体的实现,那么界面就会膨胀。避免构建未使用的灵活性的代码更容易理解,因为如果没有界面模糊您只有一个实现的事实,您知道您正在处理的具体类型,并且代码更容易理解约。

这也是我最喜欢的反对模拟的论据之一:它在你的代码中引入了人工需求,比如能够拥有多个实现的东西,你只能实现一个“真正的”实现。

答案 12 :(得分:0)

任何事情到了被痴迷的地方是错误的。这些只是工具,我们是主人。