为每个类提取一个接口是最佳实践吗?
我见过代码中每个类都有一个它实现的接口。
有时,它们没有通用的接口。
它们就在那里,并且被用来代替具体的物体。
它们不为两个类提供通用接口,并且特定于该类解决的问题的领域。
有理由这样做吗?
I have seen code where every class has an interface that it implements.
Sometimes there is no common interface for them all.
They are just there and they are used instead of concrete objects.
They do not offer a generic interface for two classes and are specific to the domain of the problem that the class solves.
Is there any reason to do that?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
为什么需要接口?切实而深入地思考。接口并不真正附加到类,而是附加到服务。接口的目标是允许其他人使用您的代码而不向他们提供代码。所以它涉及到服务及其管理。
再见
Why do you need interfaces? Think practically and deeply. Interfaces are not really attached to classes, rather they are attached to services. The goal of interface is what you allow others to do with your code without serving them the code. So it relates to the service and its management.
See ya
不会。
接口对于具有复杂行为的类很有用,如果您希望能够创建该接口的模拟或假实现类以在单元测试中使用,那么接口尤其方便。
但是,某些类没有很多行为,可以更像值一样对待,并且通常由一组数据字段组成。为这样的类创建接口没有什么意义,因为当模拟或提供接口的替代实现没有意义时,这样做会带来不必要的开销。例如,考虑一个类:
您不太可能希望接口
IColined
与此类配合使用,因为除了简单地获取和设置X< 之外,以任何其他方式实现它没有什么意义。 /code> 和
Y
值。但是,
您可能需要一个用于
RoutePlanner
的IRoutePlanner
接口,因为有许多不同的算法可用于规划路线。另外,如果您有第三个类:
通过为
RoutePlanner
提供一个接口,您可以为RobotTank
编写一个测试方法,该方法使用模拟RoutePlanner
创建一个测试方法> 只返回一个没有特定顺序的坐标列表。这将允许测试方法检查坦克是否在坐标之间正确导航,而无需测试路线规划器。这意味着您可以编写一个仅测试一个单元(坦克)的测试,而无需测试路线规划器。不过,您会发现,将真实坐标输入到这样的测试中非常容易,而无需将它们隐藏在
IColined
界面后面。No.
Interfaces are good for classes with complex behaviour, and are especially handy if you want to be able to create a mock or fake implementation class of that interface for use in unit tests.
But, some classes don't have a lot of behaviour and can be treated more like values and usually consist of a set of data fields. There's little point in creating interfaces for classes like this because doing so would introduce unnecessary overhead when there's little point in mocking or providing alternative implementations of the interface. For example, consider a class:
You're unlikely to want an interface
ICoordinate
to go with this class, because there's little point in implementing it in any other way than simply getting and settingX
andY
values.However, the class
you probably would want an
IRoutePlanner
interface forRoutePlanner
because there are many different algorithms that could be used for planning a route.Also, if you had a third class:
By giving
RoutePlanner
an interface, you could write a test method forRobotTank
that creates one with a mockRoutePlanner
that just returns a list of coordinates in no particular order. This would allow the test method to check that the tank navigates correctly between the coordinates without also testing the route planner. This means you can write a test that just tests one unit (the tank), without also testing the route planner.You'll see though, it's quite easy to feed real Coordinates in to a test like this without needing to hide them behind an
ICoordinate
interface.重新审视这个答案后,我决定稍微修改一下。
不,为每个类提取接口并不是最佳实践。这实际上可能适得其反。然而,接口很有用,有几个原因:
为了实现这些目标,接口被认为是良好的实践(并且实际上是最后一点所必需的)。根据项目规模,您会发现您可能永远不需要与接口对话,或者由于上述原因之一而不断提取接口。
我们维护一个大型应用程序,其中有些部分很棒,但有些部分却缺乏关注。我们经常发现自己重构以从类型中提取接口以使其可测试,或者因此我们可以更改实现,同时减少更改的影响。如果您对公共 API 不严格,我们这样做还可以减少具体类型可能意外施加的“耦合”效应(接口只能代表公共 API,因此对我们来说本质上变得相当严格)。
也就是说,可以在没有接口的情况下抽象行为,并且可以在不需要接口的情况下测试类型,因此它们不是上述的要求。只是您可能用来支持您完成这些任务的大多数框架/库都将针对接口有效运行。
I'll leave my old answer for context.
接口定义公共契约。实现接口的人必须实现这个契约。消费者只能看到公共合同。这意味着实施细节已从消费者手中抽象。
如今,对此的直接使用是单元测试。接口很容易被模拟、存根、伪造,凡是你能想到的。
另一个直接使用是依赖注入。给定接口的注册具体类型被提供给使用该接口的类型。该类型并不具体关心实现,因此它可以抽象地请求接口。这允许您在不影响大量代码的情况下更改实现(只要合约保持不变,影响范围就非常小)。
对于非常小的项目,我倾向于不打扰,对于中型项目,我倾向于在重要的核心项目上打扰,而对于大型项目,几乎每个类都有一个接口。这几乎总是为了支持测试,但在某些情况下是注入行为或抽象行为以减少代码重复。
After revisiting this answer, I've decided to amend it slightly.
No, it's not best practice to extract interfaces for every class. This can actually be counterproductive. However, interfaces are useful for a few reasons:
For achieving these goals, interfaces are considered good practice (and are actually required for the last point). Depending on the project size, you will find that you may never need talk to an interface or that you are constantly extracting interfaces for one of the above reasons.
We maintain a large application, some parts of it are great and some are suffering from lack of attention. We frequently find ourselves refactoring to pull an interface out of a type to make it testable or so we can change implementations whilst lessening the impact of that change. We also do this to reduce the "coupling" effect that concrete types can accidentally impose if you are not strict on your public API (interfaces can only represent a public API so for us inherently become quite strict).
That said, it is possible to abstract behaviour without interfaces and possible to test types without needing interfaces, so they are not a requirement to the above. It is just that most frameworks / libraries that you may use to support you in those tasks will operate effectively against interfaces.
I'll leave my old answer for context.
Interfaces define a public contract. People implementing interfaces have to implement this contract. Consumers only see the public contract. This means the implementation details have been abstracted away from the consumer.
An immediate use for this these days is Unit Testing. Interfaces are easy to mock, stub, fake, you name it.
Another immediate use is Dependency Injection. A registered concrete type for a given interface is provided to a type consuming an interface. The type doesn't care specifically about the implementation, so it can abstractly ask for the interface. This allows you to change implementations without impacting lots of code (the impact area is very small so long as the contract stays the same).
For very small projects I tend not to bother, for medium projects I tend to bother on important core items, and for large projects there tends to be an interface for almost every class. This is almost always to support testing, but in some cases of injected behaviour, or abstraction of behaviour to reduce code duplication.
让我引用 OO 大师 Martin Fowler 的话,为这个帖子中最常见的答案添加一些坚实的理由。
这段摘录来自“企业应用程序架构模式”(列入“编程经典”和/或“每个开发人员必读”书籍类别)。
回答你的问题:不,
我自己见过一些这种类型的“花哨”代码,开发人员认为他很可靠,但实际上难以理解,难以扩展并且过于复杂。
Let me quote OO guru, Martin Fowler, to add some solid justification to the most common answer in this thread.
This excerpt comes from the "Patterns of Enterprise Application Architecture" (enlisted in the "classics of programming" and\or the "every dev must read" book category).
Answering your question: no
I've seen some of the "fancy" code of this type myself, where developer thinks he's SOLID, but instead is unintelligible, difficult to extend and too complex.
为项目中的每个类提取接口没有任何实际原因。那就太过分了。他们必须提取接口的原因是他们似乎实现了 OOAD 原则“编程到接口,而不是实现”。您可以通过示例此处找到有关此原则的更多信息。
There's no practical reason behind extracting Interfaces for each class in your project. That'd be an over-kill. The reason why they must be extracting interfaces would be the fact that they seem to implement an OOAD principle "Program to Interface, not to Implementation". You can find more information about this principle with an example here.
拥有接口和接口编码使得更换实现变得更加容易。这也适用于单元测试。如果您正在测试一些使用该接口的代码,您可以(理论上)使用模拟对象而不是具体对象。这使您的测试更加集中且粒度更细。
从我所看到的情况来看,在实际的生产代码中切换测试(模拟)的实现更为常见。是的,它对单元测试很感兴趣。
Having the interface and coding to the interface makes it a ton easier to swap out implementations. This also applies with unit testing. If you are testing some code that uses the interface, you can (in theory) use a mock object instead of a concrete object. This allows your test to be more focused and finer grained.
It is more common from what I have seen to switch out implementations for testing (mocks) then in actual production code. And yes it is wroth it for unit testing.
我喜欢可以在时间或空间上以两种不同方式实现的事物的接口,即将来可以以不同的方式实现,或者在代码的不同部分有 2 个不同的代码客户端这可能需要不同的实现。
代码的原始编写者可能只是机器人编码,或者他们很聪明,正在为版本弹性做准备,或者为单元测试做准备。更可能是前者,因为版本弹性是一种不常见的需求——(即部署客户端且无法更改的位置,并且将部署必须与现有客户端兼容的组件)
我喜欢依赖项值得隔离的事物的接口来自我计划测试的其他一些代码。如果这些接口也不是为了支持单元测试而创建的,那么我不确定它们是个好主意。接口的维护成本很高,当需要使一个对象可与另一个对象交换时,您可能希望接口仅应用于少数方法(以便更多的类可以实现该接口),最好使用抽象类(以便可以在继承树中实现默认行为)。
所以预先需要接口可能不是一个好主意。
I like interfaces on things that could be implemented two different ways, either in time or space, i.e. either it could be implemented differently in the future, or there are 2 different code clients in different parts of the code which may want a different implementation.
The original writer of your code might have just been robo coding, or they were being clever and preparing for version resilience, or preping for unit testing. More likely the former because version resilience an uncommon need-- (i.e. where the client is deployed and can't be changed and a component will be deployed that must be compatible with the existing client)
I like interfaces on things that are dependencies worth isolation from some other code I plan to test. If these interfaces weren't created to support unit tests either, then I'm not sure they're such a good idea. Interface have a cost to maintain and when it comes time to make an object swappable with another, you might want to have an interface apply to only a few methods (so more classes can implement the interface), it might be better to use an abstract class (so that default behaviors can be implemented in an inheritance tree).
So pre-need interfaces is probably not a good idea.
If 是依赖倒置原则的一部分。基本上,代码取决于接口而不是实现。
这使您可以轻松地换入和换出实现,而不会影响调用类。它允许更松散的耦合,从而使系统的维护更加容易。
随着您的系统不断发展并变得更加复杂,这一原则变得越来越有意义!
If is a part of the Dependency Inversion principle. Basically code depends on the interfaces and not on the implementations.
This allows you to easy swap the implementations in and out without affecting the calling classes. It allows for looser coupling which makes maintenance of the system much easier.
As your system grows and gets more complex, this principle keeps making more and more sense!
我认为这对每个班级来说都不合理。
问题在于您期望从什么类型的组件中获得多少重用。当然,您必须计划比您目前真正要使用的更多重用(无需稍后进行重大重构),但是为程序中的每个类提取抽象接口将意味着您拥有的类少于需要。
I don't think it's reasonable for Every class.
It's a matter of how much reuse you expect from what type of a component. Of course, you have to plan for more reuse (without the need to do major refactoring later) than you are really going to use at the moment, but extracting an abstract interface for every single class in a program would mean you have less classes than needed.
接口定义了一种行为。如果您实现一个或多个接口,那么您的对象的行为就像一个或其他接口所描述的那样。这允许类之间的松散耦合。当您必须用另一个实现替换一个实现时,它非常有用。类之间的通信应始终使用接口来完成,除非类之间确实紧密绑定。
Interfaces define a behaviour. If you implement one or more interfaces then your object behaves like the one or other interfaces describes. This allows loose coupling between classes. It is really useful when you have to replace an implementation by another one. Communication between classes shall always be done using interfaces excepting if the classes are really tightly bound to each other.
如果您想确保将来能够注入其他实现,可能会有。对于某些(也许是大多数)情况来说,这有点矫枉过正,但就像大多数习惯一样 - 如果您习惯了,您就不会浪费太多时间这样做。由于您永远无法确定将来要替换什么,因此在每个类上提取接口确实有其意义。
问题从来没有只有一种解决方案。因此,同一接口总是可能有多个实现。
There might be, if you want to be sure to be able to inject other implementations in the future. For some (maybe most) cases, this is overkill, but it is as with most habits - if you're used to it, you don't loos very much time doing it. And since you can never be sure what you'll want to replace in the future, extracting an interface on every class does have a point.
There is never only one solution to a problem. Thus, there could always be more than one implementation of the same interface.
这可能看起来很愚蠢,但这样做的潜在好处是,如果在某个时候您意识到有更好的方法来实现某个功能,您可以编写一个实现相同接口的新类,并将一行更改为让所有代码都使用该类:分配接口变量的行。
这样做(编写一个实现相同接口的新类)还意味着您始终可以在新旧实现之间来回切换以比较它们。
最终您可能永远不会利用这种便利,并且您的最终产品实际上只使用为每个接口编写的原始类。如果真是这样,那就太好了!但编写这些接口确实不需要花太多时间,而且如果您需要它们,它们会为您节省大量时间。
It might seem silly, but the potential benefit of doing it this way is that if at some point you realize there's a better way to implement a certain functionality, you can just write a new class that implements the same interface, and change one line to make all of your code use that class: the line where the interface variable is assigned.
Doing it this way (writing a new class that implements the same interface) also means you can always switch back and forth between old and new implementations to compare them.
It may end up that you never take advantage of this convenience and your final product really does just use the original class that was written for each interface. If that's the case, great! But it really didn't take much time to write those interfaces, and had you needed them, they would've saved you a lot of time.
这些接口很好,因为您可以在(单元)测试时模拟类。
我至少为所有涉及外部资源(例如数据库、文件系统、Web 服务)的类创建接口,然后编写模拟或使用模拟框架来模拟行为。
The interfaces are good to have since you can mock the classes when (unit-) testing.
I create interfaces for at least all classes that touches external resources (e.g. database, filesystem, webservice) and then write a mock or use a mocking framework to simulate the behavior.