简体   繁体   English

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

[英]Is it wrong to make every concrete class to inherit from an interface?

This is in response to certain comments made by Zed Shaw in his blog a long while ago. 这是对很久以前Zed Shaw在他的博客中发表的一些评论的回应。

The experts will then saunter off to implement their Flaming Tower of Babel without any comments, horribly complex mock enabled tests, making sure EVERY SINGLE CLASS HAS AN INTERFACE, and ending every class with “Impl” because, well, that's the best practice. 然后专家们会漫无目的地执行他们的Flaming Tower of Babel,没有任何评论,非常复杂的模拟测试,确保每个单一类都有一个接口,并以“Impl”结束每个班级,因为,这是最好的做法。

I use Spring and Google Guice in equal measures and I've noticed that these frameworks do use the Impl postfix but sparingly. 我以相同的方式使用Spring和Google Guice,我注意到这些框架确实使用了Impl后缀,但是很少。 In my code, I use interfaces everywhere because I've been told that it makes it easier to mock etc. Do I have a naive understanding of the issue ? 在我的代码中,我到处使用接口,因为我被告知它使模拟等更容易。我对这个问题有一个天真的理解吗? (Perhaps mock frameworks can work with abstract classes or classes, I don't know. I have never tried) For my concrete implementations, I have chosen the Spring convention of prefixing the name of the implementation with the word Default. (也许模拟框架可以使用抽象类或类,我不知道。我从未尝试过)对于我的具体实现,我选择了Spring的约定,在实现的名称前加上单词Default。

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

What is the best practice on this issue ? 这个问题的最佳做法是什么?

UPDATE1 I find it useful to return an interface from a method because I can always return an anonymous class. UPDATE1我发现从方法返回一个接口很有用,因为我总是可以返回一个匿名类。

It's more than just mocks. 这不仅仅是嘲笑。 In the case of Spring, it's about dynamic proxies and aspect-oriented programming. 在Spring的情况下,它是关于动态代理和面向方面的编程。 That's how Spring does transactions, remoting, and aspects of all types. 这就是Spring如何处理所有类型的事务,远程处理和方面。

I use interfaces for repositories, services, and the like, but I don't put interfaces on model objects or anything else whose implementation isn't likely to change. 我使用接口来存储库,服务等,但是我没有将接口放在模型对象或其他任何实现不太可能改变的地方。

Interfaces separate APIs from how they're implemented. 接口将API与它们的实现方式分开。 If your implementation doesn't change, an interface doesn't make much sense. 如果您的实现没有改变,那么界面没有多大意义。

Zed Shaw is a terrific writer and developer, but take what he says with a grain of salt. Zed Shaw是一位了不起的作家和开发人员,但他带着他的话说了几句。 I think some of the over the top hyperbole that he indulges in is for entertainment value. 我认为他沉迷的一些夸张的夸张是娱乐价值。 He's got a point ("Don't do things just because somebody who claims to be an authority tells you it's a 'best practice'"), but the way he's saying it is part theater. 他有一个观点(“不要做事只是因为有人自称是一个权威人士告诉你这是'最佳实践'”),但他所说的方式是部分剧院。

The best practice is to: 最佳做法是:

  • Pick a convention and be consistent about it; 选择一个约定并保持一致; and
  • Don't go overboard with making everything implement an interface. 不要过度使用一切实现接口。

The book Interface Oriented Design has a helpful way of thinking about interfaces - that they belong to the client, not the provider. 面向接口的设计有一种有用的思考接口的方式 - 它们属于客户端,而不是提供者。 So rather than thinking about whether a given class should be represented by an interface, think about what interfaces a given class might want to interact with, then find or write classes that conform to those interfaces. 因此,不考虑给定类是否应该由接口表示,而是考虑给定类可能要与之交互的接口,然后查找或编写符合这些接口的类。 An interface may be very pared-down, compared to a longer list of public features in an implementing class - and of course a given class can implement several interfaces. 与实现类中较长的公共特性列表相比,接口可能非常简化 - 当然,给定的类可以实现多个接口。 Looking for a one-to-one correspondence between classes and interfaces, even thinking about interfaces as representations of full classes, isn't as useful a way of thinking about interfaces. 寻找类和接口之间的一对一对应关系,甚至将接口视为完整类的表示,并不是一种考虑接口的有用方法。

This is largely a subjective matter. 这在很大程度上是一个主观问题。 The best response is probably due what you can to enable as much unit testing of your code as possible but don't let interface creation get in the way of getting things done. 最好的响应可能是由于您可以尽可能多地对代码进行单元测试,但不要让界面创建妨碍完成任务。 If you code is quickly getting swamped trim it down. 如果您的代码很快被淹没,请将其修剪下来。

That said, I've seen code where there are 400 classes and 400+ interfaces for those classes. 也就是说,我已经看到代码中有400个类和400多个接口用于这些类。 Just digging through the pile of code with its poor naming conventions was enough to give me the willies. 只是通过其糟糕的命名约定来挖掘一堆代码就足以让我有所了解。 On the flip side I'm dealing with a third-party proprietary library with no interfaces. 另一方面,我正在处理没有接口的第三方专有库。 Mocking these objects is getting to be a pain. 嘲弄这些物体会变得很痛苦。

I don't see any advantage to creating an interface for every class. 我认为为每个类创建一个接口没有任何好处。 That just forces you to write a bunch of code twice. 这只会迫使你两次编写一堆代码。

I don't see a need to create an interface to distinguish an API from an implementation. 我认为不需要创建一个接口来区分API和实现。 The list of public functions of a class with their signatures is the API. 具有签名的类的公共函数列表是API。 If the class requires worker-bee functions that are not part of the API, then they shouldn't be public. 如果该类需要不属于API的worker-bee函数,则它们不应该是公共的。 I don't see that 我没有看到

I create an interface when, in the given context, it serves a useful purpose. 我在给定的上下文中创建一个接口,它有用的目的。 Basically this means: 基本上这意味着:

(a) In practice, my most common reason is to implement/simulate multiple inheritance. (a)在实践中,我最常见的原因是实现/模拟多重继承。 If I need A extends B but I also need to have a function that accepts a C and I want to pass A to that function, then I must make either B or C an interface rather than a parent class and make it A extends B implements C or vice versa. 如果我需要A扩展B但我还需要一个接受C的函数,我想将A传递给该函数,那么我必须使B或C成为一个接口而不是父类并使它成为一个扩展B实现C或反之亦然。

(b) When I am creating an API for a library or utility function that will be implemented by something outside that library or utility, usually by multiple somethings. (b)当我为库或实用程序函数创建API时,该函数将由该库或实用程序之外的东西实现,通常由多个事物实现。 The "Comparable" interface in the Java library would be an example of this. Java库中的“Comparable”接口就是一个例子。 A sort utility needs a function to compare a pair of objects. 排序实用程序需要一个函数来比较一对对象。 When a program calls the sort utility with objects defined in the calling program, the sort utility can't be expected to know how to compare them, so the calling program must provide a compare function. 当程序使用调用程序中定义的对象调用sort实用程序时,不能指望sort实用程序知道如何比较它们,因此调用程序必须提供compare函数。 The signature for the compare function logically goes in an Interface that the caller can implement for its objects. 比较函数的签名在逻辑上位于调用者可以为其对象实现的接口中。

(c) If I want to publish an interface without revealing the implementation. (c)如果我想发布一个接口而不透露实现。 This might be for proprietary reasons, but in practice it's usually because another person or team will implement the interface. 这可能是出于专有原因,但实际上通常是因为另一个人或团队将实现该接口。

Any class that provides a 'service' to another collaborator should have an interface - you would potentially want to chop and change implementations to achieve the desired behavior. 任何为另一个协作者提供“服务”的类都应该有一个接口 - 您可能希望切断并更改实现以实现所需的行为。 As an example you could replace a twitter service with an email service easily. 例如,您可以轻松地将Twitter服务替换为电子邮件服务。

Any class that represents a 'value' object should not need an interface as it serves a single purpose. 任何表示“值”对象的类都不需要接口,因为它只用于单个目的。 In testing you would not even need to mock these objects, just used them directly as they define leaf nodes of your object graph. 在测试中,您甚至不需要模拟这些对象,只需在它们定义对象图的叶节点时直接使用它们。

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

Having interfaces on objects, and having objects only talk to each other through interfaces, is a good thing. 在对象上有接口,并且对象只能通过接口相互通信,这是一件好事。

Note that I do mean objects, and not collections or other things that are essentially 'data'. 请注意,我的意思是对象,而不是集合或其他本质上是“数据”的东西。

However, having a class implement 'IClassName' or similar, and having that pattern throughout a codebase reflects a poor understanding of the concepts of an interface. 但是,具有类实现'IClassName'或类似的类,并且在整个代码库中具有该模式反映了对接口概念的不良理解。 Interfaces should generally be declared by the consuming class, as a declaration of 'hey, here's the services I need.' 接口通常应该由消费类声明,作为'嘿,这是我需要的服务'的声明。 In that way, an interface represents the interactions between two objects, not a single view of the object to the world. 这样,界面表示两个对象之间的交互,而不是对象与世界的单个视图。 It also helps keep responsibilities separate, as each class is dealing with its own ideal interfaces, and so can help keep domain logic separate from implementation logic. 它还有助于将职责分开,因为每个类都在处理自己的理想接口,因此可以帮助保持域逻辑与实现逻辑分离。

(edit) (编辑)

It's really about the difference between indirection, and abstraction. 它实际上是关于间接和抽象之间的区别。 An interface that matches your API directly is simply indirection. 直接匹配API的接口就是间接接口。 An interface that matches the needs of the consumer directly is an abstraction, as it declares what the consumer wants done, and hides all information of how it is done. 符合消费者需求的接口直接是一种抽象的,因为它宣称消费者想要做的事情 ,并且隐藏了它是如何做的所有信息。

Zed Shaw has a good article on this. Zed Shaw有一篇很好的文章。

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

I don't think that you can go really wrong by hiding all your concrete classes behind interfaces. 通过隐藏接口后面的所有具体类,我认为你不会出错。

There are cases where it's superfluous, but it doesn't cost you much. 有些情况下它是多余的,但它不会花费你太多。

One can even make a case for making Domain Objects implement interfaces. 甚至可以使域对象实现接口。 Benefits: 优点:

  • You can hide the ugly details from the client code (eg JPA annotations, setters to be used by the ORM framework only) 您可以隐藏客户端代码中的丑陋细节(例如,JPA注释,仅由ORM框架使用的setter)
  • Just like with DAOs or Services, you can program against abstractions. 就像DAO或服务一样,您可以针对抽象进行编程。 Think of a User abstraction – your business logic does not depend on how and where they are stored (eg DB or OS), and you can easily use an Adapter pattern if you have to work with third-party code. 考虑用户抽象 - 您的业务逻辑不依赖于它们的存储方式和位置(例如DB或OS),如果您必须使用第三方代码,则可以轻松使用适配器模式。

I think it depends on your design. 我认为这取决于你的设计。 If you want to use dependency injection or mock object testing, then interfaces make sense. 如果您想使用依赖注入或模拟对象测试,那么接口是有意义的。 Also, if you plan on having multiple implementations, then interfaces are definitely required to state the contract for the behavior. 此外,如果您计划进行多个实现,那么肯定需要接口来声明行为的合同。

When in doubt, perhaps take the agile approach? 如果有疑问,或许采取敏捷方法?

Just need a beer bottle opener 只需要一个啤酒开瓶器

class BeerBottleOpener // life's good

Now need a wine bottle opener 现在需要一个酒瓶开瓶器

class WineBottleOpener // life's still good

Refactor a little 重构一点

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

Btw, for mock testing, I've used EasyMock which requires interfaces. 顺便说一句,对于模拟测试,我使用了需要接口的EasyMock Now switched to Mockito which can mock classes and is more explicit and slightly simpler. 现在切换到Mockito ,可以模拟类,更明确,更简单。

Going overboard on interfaces is a very real possibility. 在接口上过分是非常现实的可能性。 Ignoring the [neglible] performance penalty of making every method call using virtual dispatch it also introduces more options into the system. 忽略使用虚拟调度进行每个方法调用的[可忽略]性能损失,它还会在系统中引入更多选项。 It can actually make it harder to debug and secure the software. 它实际上可以使调试和保护软件变得更加困难。 If your method/function accepts any old interface implementor you've made a decision to accept that the implementor may have implemented the interface wrong or perhaps even maliciously. 如果您的方法/函数接受任何旧的接口实现者,则您已决定接受实现者可能已经错误地或者甚至恶意地实现了接口。 How your program/library/framework allows variation is an important part of the design process. 您的程序/库/框架如何允许变化是设计过程的重要部分。

Giving everything an interface (or more generally designing for flexibility that will probably go unused) is called overengineering. 给所有东西一个接口(或更一般地设计可能未使用的灵活性)称为过度工程。 If you're likely never going to have more than one concrete implementation of something, an interface is just bloat. 如果你可能永远不会有多个具体的实现,那么界面就会膨胀。 Code that avoids building in unused flexibility is easier to understand, because without the interface obscuring the fact that you only have one implementation, you know what concrete type you're dealing with and the code is easier to reason about. 避免构建未使用的灵活性的代码更容易理解,因为如果没有界面模糊您只有一个实现的事实,您就知道您正在处理的具体类型以及代码更容易推理。

This is also one of my favorite arguments against mocking: It introduces artificial requirements into your code, such as being able to have more than one implementation of things that you will only ever have one "real" implementation of. 这也是我最喜欢反对模拟的论据之一:它在你的代码中引入了人工需求,例如能够拥有多个实现的东西,你只能实现一个“真正的”实现。

Doing anything to the point of obsession is wrong. 任何事情以至于经过考验都是错误的。 These are just tools, we are the masters. 这些只是工具,我们是主人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM