在我工作的公司中,每个服务类别都有一个对应的接口。
这有必要吗?

注意:


这些接口中的大多数仅由单个类使用

我们不创建任何类型的公共API

现代的模拟库能够模拟具体的类,而IDE只需单击两次即可从类中提取接口,这仅仅是从前的保留吗?

评论

也许它们是由某些代码生成工具生成的?

@gnat -没有更多地证明设计选择的必要性,而不是测试类是否实现了“ foo”功能的证明就表明了将该功能称为“ foo”的必要性。通过编写描述的测试,您正在检查是否正在实施某种协议,而不是需要该协议。如果如OP所述,如果您可以轻松地在没有接口的情况下编写程序,则测试仅根据架构师的选择而有效还是无效。

@AndresF。请参阅我对CrazyEddie的评论中的解释;测试只是保护当前的实现免受破坏(首先是我自己-因为当我在内部API中看到具有单个实现的接口时,我的本能是摆脱这种情况)。

@gnat-再一次,您如何证明需要在两个类之间建立接口,而不是直接耦合它们?我只是看不到仅检查类是否实现了接口就可以说明这种抽象的必要性。您声称应该先进行单元测试,以证明其必要性。您尚未对这种说法有任何了解。

什么是“服务等级”?

#1 楼

您的公司遵循SOLID原则,并且以接口(而不是具体类)为目标,这为程序增加了零开销。他们正在做的是非常少量的额外工作,当您做出的假设最终是错误的时,这些工作可以偿还大量交易……而您永远无法说出将要发生的假设。您的建议虽然工作量稍少,但可能会花费很多很多小时的重构工作,而这些重构工作只需遵循基本的OO设计原则就可以提前完成。

您应该学习这些家伙,并认为自己很幸运。希望我在这样的环境中工作,即以坚实的设计为优先目标。

评论


仅仅因为今天似乎没有实用工具,并不意味着它就没有实用工具。

– DaveE
2012年5月24日18:21

可能已经有实用程序。我不知道他们是否这样做,但是一个实现可以实现许多接口。如果将接口最小化为给定类型的客户端所需的最少功能,则这将使代码能够解释那些客户端,而不必了解未完全使用的较大接口。它还可以阻止跨概念入侵,在这种情况下,原本打算以一种方式使用对象的客户端开始使用不应使用的功能。例如,开始使用模型的可变接口的视图……可能不应该。

–爱德华·古奇
2012年5月24日18:34

@DaveE:YAGNI说你错了。

– DeadMG
2012年5月26日16:10

每当我将SOLID用作理由时,我都会忍不住认为设计根本没有道理。可以肯定的是,如果有被诉人会给出更具体的原因。

–乔纳森·艾伦
2012年9月4日在6:48

@Jonathan与“因为这是最佳实践”相同,它大致等同于“我不知道,但有人说这是个好主意”

–Richard Tingle
15年7月26日在18:13

#2 楼

考虑到您的问题,我认为没有记录这种设计的原因。
不合理地使用具有单个实现的接口是完全错误的,因为这违反了YAGNI。以我的经验,这在维护方面也相当有害,主要是因为实现接口的方法被迫公开为不必要的方法。积累了更多的重构经验之后,您可能会学会欣赏private和package private访问修饰符的适度魅力,使人们可以将注意力集中在单个类/包上。

习惯用法,在我看来,它是一样糟糕的-首先是因为它不允许维护人员“从坏到坏”。结果,这在不太吸引人的选择之间留下了一个。选项是,要么按原样保留可疑的公共资料,从而每次需要更改代码时都反复降低生产力,要么通过盲目“折叠”单个实现以使其更易于维护,以进行风险性更改-将自我暴露于可能会痛苦地发现这是错误的方式,还有其他(上帝禁止,远程)模块不希望看到该接口的风险。单一实现接口几次。每次都有相当的教育经验,但我真的不想再次到达那里。事实是,这是在后期测试,集成甚至在用户接受时发生的,并且回滚/修复很久以前在此阶段犯下的错误非常麻烦。



一个不容提及的选择是调查在发现无证使用之后是否合理(然后分别是折叠或记录)。

对我来说,这一做法适得其反。这种方法本质上迫使人们进行全面集成测试,这可能是在某些复杂的修复/重构过程中中断流程的最有效方法之一。





我只是看不到实用性...接口像com.company.service.foocom.company.service.foo.impl一样一对一地使用


下面的内容是关于我通常是如何处理此类情况的。评论这会损害可维护性的解释,并指出如果没有文档,这看起来像是过度设计。添加一条评论,建议通过将其“折叠”到POJO中来解决此问题,以实现更容易的维护性。我至少要等待一两个星期。


如果有人解释了这个目的,a)将其放入故障单中,b)将它添加到PRJ-123 javadocs中,类似于故障单中说明的接口用途PRJ-123,c)关闭车票,d)跳过接下来的三个步骤。
否则,...


请团队负责人或经理问我如果按照建议修好机票会如何看待。请选择一位有足够权限的顾问如果建议的修补程序出现错误,请进行“尖叫测试”。 )。
等到更改通过全面测试并根据其结果更新票证-添加有关更改是否通过测试的信息。
创建并处理新的问题跟踪票证以进行处理根据您的试票结果(Foo)得出的其他类似情况:记录目的或将其分解为POJO。



评论


的确,方法被迫公开,但是接口本身可以是公开的,受保护的,封装私有的或私有的。我可以想象创建一个私有接口(嵌套在另一个类中),该接口由2个或更多类(也嵌套在同一类中)实现。如果您只打算拥有1个实现类,那么何必麻烦一下。但是私有接口可以避免您描述的生产力陷阱。

–emory
2012年5月24日23:59

@emory伟大的思想家有同感;我非常喜欢私有/包私有接口。使这些功能特别出色的一件事是,仅在类/包中,就可以确定其目的

– gna
2012年5月25日晚上8:28

即使结合支持实践,YAGNI也不被普遍接受为有效原则

–奥斯卡
13年3月12日在16:52

我猜可能比研究代码查找接口并消除它们更有效率。

–user1455836
15年1月23日,9:50

闲聊与此无关。不合理使用的危害是真实存在的:实现接口的方法被迫公开为不必要的结果->结果是,仅在类/包中看不到它们的目的。经历整个大型项目而不是研究单个类或一揽子计划,这对生产力造成了相当大的打击

– gna
16年7月7日在7:02



#3 楼

以下是Adam Bien对这个问题的看法:Service s = new ServiceImpl()-为什么要这样做?


这不仅膨胀(与“ Convention Over Configuration”相反) ”),但会造成真正的损害:


想象一下,您得到了另一个实现(即接口的整个要点)-您将如何命名?
它将两倍大量的工件-并显着增加了“复杂性”
阅读接口和impl上的javadocs真的很有趣-另一个冗余
IDE中的导航不太流畅

/>

,我也同意他的看法。在Internet上有关J2EE艰难时期的旧教程。

评论


如果您分发不同的服务,那么您也应该能够像这样命名它们。如果您不能使用其他名称,则将其视为现有服务之外的不必要的服务。 UserService,AuthService等。另一个非常简单的示例是Java中的List <>接口。

–安娜·克莱恩(Anna Klein)
19年5月2日在20:30



您实际上不必在接口和类上重复注释,您可能想使用{@inheritDoc}标签。

–mneri
20-2-11在10:10



#4 楼

Java接口不仅仅涉及可模拟性(当然,这是一个因素)。接口公开了服务的公共合同,没有实现细节,例如私有方法或属性。

请注意,使用内部服务时(如您所用),“公共”是指该服务的实际用户服务,包括但不限于其他程序员和内部服务!

评论


答案实际上并不比这复杂。接口代表一种约定,并启用依赖项注入和模拟,而无需过多和低效地使用许多框架为类注入和模拟提供的反射。

– Maple_shaft♦
2012年5月25日下午2:16

public关键字在公开我课程的公共合同方面做得很好。

–乔纳森·艾伦
2012年9月4日下午6:50

@JonathanAllen首先,所有接口在Java中都是抽象的:)其次,您看到的是错误的结果:不是必须从实现中消除杂乱的地方,而是接口本身(即,从“合同”中)。实现不是契约,接口是契约。您的目的是使合同保持“完整”。使用类很难做到这一点,因为您还拥有不属于公共合同的“非合同”方法(在您的示例中为私有)。

– Andres F.
2012年9月4日在20:29

@JonathanAllen(续)但是说您的IDE可以隐藏私有方法和属性,从而有效地为您提供公共合同的视图。如果您的班级仅实现一个合同,这将起作用,但是如果班级一次实现两个合同会发生什么呢?您如何只提供一个合同的视图,却隐藏另一个合同,有效地“弄乱”合同呢?做到这一点最干净的方法是将每个合同作为一个单独的接口,正是您拒绝的方法;)

– Andres F.
2012年9月4日20:31



@JonathanAllen最后,Java接口比public关键字更好地描述了意图。很明显,接口中可用的是定义类型的东西。相反,谁知道为什么给定方法是公开的?也许程序员只是忘了将它设为私有(您可以打赌这会发生!)

– Andres F.
2012年9月4日20:35



#5 楼

每个服务类都具有接口的一个可能原因是,它使与复杂框架(例如Spring,JEE)的交互变得更加简单。这是因为Java可以简单地针对接口生成拦截器对象(请参阅java.lang.reflect.Proxy);在使用具体的类时,这样做更加棘手,并且在使用多个不同的类加载器时充满了一些不明显的警告。
如果您使用OSGi,这将更加正确,因为这些OSGi最终将这些服务接口从另一个加载这些服务的实现中的classloader。这(与服务发现结构一起)反过来在每个服务与其客户之间实现了非常强的分离。

请注意,即使IDE只需单击一下即可从类中提取接口,这并不意味着它一定会提取正确的接口。接口。仍然需要情报。

评论


我不太确定“老练”是否正确。我认为笨拙更准确。但是,如果您已经决定使用其中之一,则无需争论。必须使用抽象接口。

–乔纳森·艾伦
2012年9月4日在18:32



“请注意,即使IDE只需单击一下即可从类中提取接口,这并不意味着它必须提取正确的接口。仍然需要智能。”我尚未找到一个机会通过取消标记不需要的方法来自动执行此操作。

–麦哲伦
18年4月29日在13:15

#6 楼

最初使用Interface似乎有些开销。但是后来,当我们需要扩展某些功能时,这些Interface会有很大帮助。

如果仅执行具体实现,则必须更改代码实现。.但是,通过使用接口,您只需要添加一个实现接口协定的新类即可(可以调用新的类对象)来自旧界面的参考变量)。

评论


你在圈子里说话。要说明您的情况,您需要一个具体的例子。

–乔纳森·艾伦
2012年9月4日在6:51

然后,我可以首先不创建Service / ServiceImpl吗。然后,当需要重构时,我将类XService更改为接口XService并创建类XServiceImpl实现XService?请问有什么问题吗?谢谢!

–ch271828n
20 Mar 7 '20 at 10:40