在Java中,对于复杂模型,使用接口是否会提高性能?

4
标题难以理解,但我不确定如何用其他方式概括。欢迎进行任何澄清编辑。
我被告知并建议使用接口来提高性能,即使在不特别需要常规“接口”角色的情况下也是如此。在这种情况下,对象是具有许多方法和字段的大型模型(在MVC意义上)。
对我推荐的“好用法”是创建一个接口及其唯一实现。肯定不会有其他类实现此接口。我被告知这样做更好,因为它“暴露得更少”(或类似的东西)给将使用此类方法的其他类,因为这些对象是从其接口引用对象的(实现中的所有公共方法都在接口中复制)。
这对我来说似乎相当奇怪,因为在我看来这似乎是C++用法(带有头文件)。在Java中我看到了这一点,但是真的有必要为这样独特的实现制作接口吗?
制作这样的唯一实现接口是否真的有意义?我真的很想在这个话题上进行一些澄清,以便我可以证明不遵循这种行为以及重复所有声明所产生的麻烦。
编辑:感谢大家的回答,这对我非常有帮助和启发(不仅是“接受”的回答)。
显然,性能上没有优势,现在我对它可能带来的利益范围有了更广泛的认识(取决于情况),除了接口的通常OO角色。

不确定使用接口的主要原因是否是性能。 - R. Martinho Fernandes
@Martinho - 我知道使用接口的主要原因。但是这个“建议”让我困扰了一段时间。 - Gnoupi
10个回答

12
使用接口与性能没有太大关系(除了可能会影响开发团队的表现,即开发速度)。它更多地涉及控制依赖关系,并将程序中不相关的部分分离开来。
如果您的代码直接依赖于具体类C,那么这段代码就更难进行单元测试。如果您改为依赖于接口,则在单元测试中创建模拟实现变得轻而易举。
当然,您不需要将类的所有方法都上移至父接口中。事实上,您甚至不需要一个单一的父接口。分析该类的使用情况(特别是对于像您这样的大类),很可能会发现两个或更多不同的方法组,由不同的客户端使用(例如,一个客户端仅查询对象状态,而另一个则更新对象状态)。这使得可以创建两个或更多不同的接口,每个接口都更简单、更清晰。
这种分析甚至可能导致您得出结论:您的类试图做太多事情(而不是只有一个single responsibility),最好将其中一些内容提取到单独的类中!换句话说,一旦开始考虑并编写接口,您就会在不同的层次上看到设计,这可能会导致更好的设计解决方案。

总之,如果在上述所有分析之后,您仍然认为接口对于您的模型类没有任何用处,请做好记录。半年后再回来看看。然后,如果您仍然觉得它没有回报,就抛弃它。接口 - 就像程序的任何其他元素一样 - 应该始终具有明确的目的和存在的理由(比“我的同事告诉我要创建它”更好)。它们不是万能药。如果您巧妙地使用它们,可以使代码更好。如果您愚蠢地使用它们,那么您的代码甚至会变得更糟。您在这里发布这个问题的事实意味着您想学习如何聪明地使用它们,这是好的 :-)


我确实没有考虑过测试,但在这种情况下它可能会很有用。虽然这不是被给予我的原因,但对于我拥有的一些对象来说确实是一个有效的观点。 - Gnoupi
最多赞,最详细,良好的格式和一般性建议,谢谢。打勾 - Gnoupi

4
首先,接口没有任何性能优势。如果有什么好处的话,使用多态代码和动态派发(例如C++的虚函数)会带来一定的成本。
将接口视为约束你编写更好代码的东西,而不是增加表达力的东西。在我看来,接口之所以重要有以下三个原因:
1) 它们可以帮助你编写具有更好封装性、数据隐藏性、稳定性等特性的代码。
2)它们可以通过考虑你正在尝试表示什么而不是如何实现它来帮助你分离行为和实现。无法添加状态确实可以防止你无意中添加它。
3)它们让你能够在未来扩展事物并将代码分成独立单元。每个组件只知道其他服务的必需信息。
您可以使用没有状态的抽象类来完成其中的一些工作,但接口特别用于此,并具有多个子类型。在C++中,这样的抽象类通常用于模拟接口。
现在,如果您可以使用类直接编写完美封装级别的大型系统,那就太好了。但是,类往往会诱使您过早引入状态并创建连接。
您是否应该总是编写接口?当然不是。总是害怕“总是”规则 :) 这始终是浪费工作和无用复杂性以及好处之间的平衡。随着时间和经验的积累,您会获得这种直觉。
另外,头文件本质上是接口文件。它们通常定义允许的行为,但没有状态。如果您先学习C,然后学习Java(一些学校这样做),您将了解C中的ADT,然后使用接口了解Java中的ADT。

我确实同意这一点,写接口时不考虑具体实现,只关注它应该做什么和如何做会感觉更好。 - Gnoupi

4

简而言之 -

  1. 您需要在接口对象中明确记录该模块的API。这将更清晰地传达给用户,并可能使您考虑接口中包含什么。
  2. 您可以在同一对象上提供多个接口,并限制消费者的操作(例如只读与读写)。
  3. 模拟框架利用接口,模拟类是一种非常强大的测试技术。

1和2是相互关联的,在这个回答中确实只提到了这一点。 - Gnoupi

3
事实上,使用接口而不是类可以让您做到以下三点:
  1. 区分合同和实现:合同是接口(它声明的是您将要做的事情),而类显然是实现。
  2. 提供易于替代的实现。这种情况的典型例子包括在不修改用户代码的情况下更换存储机制(例如将MySQL替换为Project Voldemort)。
  3. 作为第2点的好处(并且这次可以轻松扩展到模型元素),接口允许您进行一种白盒测试:通过使用模拟框架(JMock、EasyMock等),您将替换接口的实现,其行为可能可疑,由所谓的“模拟”返回您期望的测试结果。这将使您的测试不仅检查一个组件,还确保错误来自该组件,而不是其内部承包商(即您给他的接口)。

2

对我来说,使用接口的原因似乎也很奇怪。我不明白有什么性能优势,如果有的话,那可能完全不重要。

尽管如此,即使目前只有一个实现,仍然有一些使用接口的理由。它可能更容易扩展或提供替代实现。(虽然在任何体面的IDE中都有提取接口的重构工具。)这可能会更容易进行测试:如果您想测试一个仅通过接口使用其他类的类,您可以轻松地为其他类提供模拟对象。


真的,我还没有考虑测试方面,它确实在这种情况下非常有用。不过对于另一点,它确实关乎那些只有一个实现的大型对象。 - Gnoupi

2

1

接口在Java中被过度使用,部分原因是由于语言设计,部分原因是人们认为创建大量接口是良好的设计。

如果Java允许多重继承,则抽象类将优于接口。Scala是一种新的JVM编程语言,具有Trait的概念,它本质上是具有MI功能的抽象类。

抽象类的优点是可以向其中添加行为而不会破坏扩展它的现有类。因此,抽象类也比接口更适合作为扩展机制(插件)。

不幸的是,JMock及其同类鼓励使用大量接口进行BDD测试,但这并不一定正确,因为至少有几个库可以模拟具体对象。


0

这个问题针对C#,但我认为它同样适用。得票最高的答案说,为每个类定义一个接口只是代码噪音


0

有一种相对常见的开发信念,即几乎每件事都应该有一个接口。据我所知,这要么是基于“针对接口编程而非实现”的神秘信仰(绝对不需要你在代码中到处添加接口),要么是使用旧版模拟框架进行测试驱动开发时无法模拟具体类的结果。

无视这种胡言乱语。


你能推荐一个可以模拟给定具体类的模拟框架吗? - Thorbjørn Ravn Andersen
@Thorbjørn:我一直在使用Groovy的内置模拟功能;但是jMock和EasyMock都有扩展,可以模拟具体类。PowerMock几乎可以模拟任何东西,包括final类和方法。 - Michael Borgwardt

0

又一种误用接口的方式?

使用接口并没有明显的性能提升。相反,由于运行时类型信息(根据编译器的特性),它可能会对性能产生负面影响。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接