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

发布于 2024-08-21 19:05:08 字数 657 浏览 7 评论 0原文

这是对 Zed Shaw 很久以前在他的博客中发表的某些评论的回应。

然后专家们将漫步前往 实施他们的巴别塔火焰 没有任何评论,非常复杂 模拟启用测试,确保每一个 单类有一个界面,并且 每堂课都以“Impl”结束 因为,嗯,那是最好的 练习。

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

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

这个问题的最佳实践是什么?

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

This is in response to certain comments made by Zed Shaw in his blog a long while ago.

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.

I use Spring and Google Guice in equal measures and I've noticed that these frameworks do use the Impl postfix but sparingly. 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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

两个我 2024-08-28 19:05:08

这不仅仅是嘲笑。就 Spring 而言,它涉及动态代理和面向方面的编程。这就是 Spring 处理事务、远程处理以及所有类型方面的方式。

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

接口将 API 与其实现方式分开。如果你的实现没有改变,接口就没有多大意义。

Zed Shaw 是一位出色的作家和开发人员,但对他所说的话持保留态度。我认为他所沉迷的一些过分夸张是为了娱乐价值。他说得有道理(“不要仅仅因为自称权威的人告诉你这是‘最佳实践’就去做事”),但他的说法有些戏剧性。

It's more than just mocks. In the case of Spring, it's about dynamic proxies and aspect-oriented programming. That's how Spring does transactions, remoting, and aspects of all types.

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. 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. 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.

霓裳挽歌倾城醉 2024-08-28 19:05:08

最佳实践是:

  • 选择一个约定并保持一致;并且
  • 不要过度让所有东西都实现接口。

The best practice is to:

  • Pick a convention and be consistent about it; and
  • Don't go overboard with making everything implement an interface.
差↓一点笑了 2024-08-28 19:05:08

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

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.

隔岸观火 2024-08-28 19:05:08

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

也就是说,我见过有 400 个类和这些类的 400 多个接口的代码。仅仅通过挖掘一堆糟糕的命名约定的代码就足以让我感到不安。另一方面,我正在处理没有接口的第三方专有库。嘲笑这些对象变得很痛苦。

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. 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.

纵情客 2024-08-28 19:05:08

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

我认为没有必要创建一个接口来区分 API 和实现。类的公共函数及其签名的列表就是 API。如果该类需要不属于 API 一部分的工蜂函数,那么它们不应该是公共的。我不认为

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

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

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

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

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. The list of public functions of a class with their signatures is the API. If the class requires worker-bee functions that are not part of the API, then they shouldn't be public. 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. 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.

(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. The "Comparable" interface in the Java library would be an example of this. 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. 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. This might be for proprietary reasons, but in practice it's usually because another person or team will implement the interface.

萤火眠眠 2024-08-28 19:05:08

任何向其他协作者提供“服务”的类都应该有一个接口 - 您可能需要修改和更改实现以实现所需的行为。例如,您可以轻松地将 Twitter 服务替换为电子邮件服务。

任何表示“值”对象的类都不需要接口,因为它只用于单一目的。在测试中,您甚至不需要模拟这些对象,只需直接使用它们,因为它们定义了对象图的叶节点。

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.

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.

深海少女心 2024-08-28 19:05:08

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

Well I 'heard' that in SmallTalk, you always start defining interfaces before the actual implementation... So I guess, it really depends on the purpose and the design objective the you want to achieve...

世界等同你 2024-08-28 19:05:08

我认为将所有具体类隐藏在接口后面不会造成真正的错误。

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

人们甚至可以提出让域对象实现接口的案例。好处:

  • 你可以隐藏丑陋的细节
    客户端代码(例如 JPA
    注释,设置器使用
    仅限 ORM 框架)
  • 就像 DAO 或服务一样,
    你可以针对
    抽象。想想一个用户
    抽象——你的业务逻辑
    不取决于方式和地点
    它们被存储(例如数据库或操作系统),
    您可以轻松使用适配器
    如果你必须使用模式
    第三方代码。

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 (e.g. JPA
    annotations, setters to be used by
    the ORM framework only)
  • Just like with DAOs or Services,
    you can program against
    abstractions. Think of a User
    abstraction – your business logic
    does not depend on how and where
    they are stored (e.g. DB or OS),
    and you can easily use an Adapter
    pattern if you have to work with
    third-party code.
横笛休吹塞上声 2024-08-28 19:05:08

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

如有疑问,是否可以采取敏捷方法?

只需要一个啤酒开瓶器

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 ,它可以模拟类,并且更明确且稍微简单。

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. Now switched to Mockito which can mock classes and is more explicit and slightly simpler.

错爱 2024-08-28 19:05:08

在对象上拥有接口,并且让对象仅通过接口相互通信,是一件好事。

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

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

(编辑)

这实际上是关于间接和抽象之间的区别。直接与您的 API 匹配的接口只是间接的。直接满足消费者需求的接口是一个抽象,因为它声明了消费者想要做什么,并隐藏了如何完成的所有信息。

Zed Shaw 有一篇关于这方面的好文章。

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

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. 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. 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.

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

水水月牙 2024-08-28 19:05:08

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

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.

离鸿 2024-08-28 19:05:08

为所有东西提供一个接口(或者更一般地说,为可能不会被使用的灵活性而设计)被称为过度设计。如果您可能永远不会对某事物有多个具体实现,那么接口只是臃肿。避免构建未使用的灵活性的代码更容易理解,因为接口不会掩盖您只有一个实现的事实,您知道您正在处理什么具体类型,并且代码更容易推理关于。

这也是我最喜欢的反对模拟的论据之一:它在代码中引入了人为的要求,例如能够拥有多个实现,而您只能拥有一个“真正”的实现。

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.

半步萧音过轻尘 2024-08-28 19:05:08

任何事情到痴迷的地步都是错误的。这些只是工具,我们才是主人。

Doing anything to the point of obsession is wrong. These are just tools, we are the masters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文