简体   繁体   English

为每个类提取接口是最佳做法吗?

[英]Is it the best practice to extract an interface for every class?

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? 有什么理由这样做吗?

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: 例如,考虑一个类:

class Coordinate
{
  public Coordinate( int x, int y);
  public int X { get; }
  public int y { get; }
}

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 setting X and Y values. 您不太可能希望接口ICoordinate与此类一起使用,因为除了简单地获取和设置XY值之外,以任何其他方式实现它都没什么意义。

However, the class 但是,班级

class RoutePlanner
{
   // Return a new list of coordinates ordered to be the shortest route that
   // can be taken through all of the passed in coordinates.
   public List<Coordinate> GetShortestRoute( List<Coordinate> waypoints );
}

you probably would want an IRoutePlanner interface for RoutePlanner because there are many different algorithms that could be used for planning a route. 你可能会想一个IRoutePlanner的接口RoutePlanner ,因为有可能被用于规划路线许多不同的算法。

Also, if you had a third class: 另外,如果你有第三堂课:

class RobotTank
{
   public RobotTank( IRoutePlanner );
   public void DriveRoute( List<Coordinate> points );
}

By giving RoutePlanner an interface, you could write a test method for RobotTank that creates one with a mock RoutePlanner that just returns a list of coordinates in no particular order. 通过为RoutePlanner一个接口,您可以为RobotTank编写一个测试方法,该方法使用模拟RoutePlanner创建一个只返回无特定顺序的坐标列表的方法。 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. 你会看到,很容易将真正的坐标输入这样的测试而不需要将它们隐藏在ICoordinate接口之后。

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: 但是,接口很有用,原因如下:

  • Test support (mocks, stubs). 测试支持(模拟,存根)。
  • Implementation abstraction (furthering onto IoC/DI). 实现抽象(进一步到IoC / DI)。
  • Ancillary things like co- and contra-variance support in C#. 辅助事物,如C#中的co-contra和contra-variance支持。

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). 我们还这样做是为了减少具体类型可能会在您的公共API不严格的情况下意外强加的“耦合”效应(接口只能代表公共API,因此对我们来说固有地变得非常严格)。

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. 这几乎总是支持测试,但在某些情况下注入行为或行为抽象以减少代码重复。

Let me quote OO guru, Martin Fowler, to add some solid justification to the most common answer in this thread. 让我引用OO大师Martin Fowler为这个帖子中最常见的答案添加一些可靠的理由。

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). 此摘录来自“企业应用程序架构模式” (参与“编程经典”和“或每个开发必读”书籍类别)。

[Pattern] Separated Interface [Pattern] 分离界面

(...) (......)

When to Use It 何时使用它

You use Separated Interface when you need to break a dependency between two parts of the system. 当您需要破坏系统的两个部分之间的依赖关系时,可以使用分离的接口。

(...) (......)

I come across many developers who have separate interfaces for every class they write. 我遇到过许多开发人员,他们为每个编写的课程都有单独的接口。 I think this is excessive, especially for application development. 我认为这是过度的,特别是对于应用程序开发。 Keeping separate interfaces and implementations is extra work, especially since you often need factory classes (with interfaces and implementations) as well. 保持单独的接口和实现是额外的工作,尤其是因为您经常需要工厂类(具有接口和实现)。 For applications I recommend using a separate interface only if you want to break a dependency or you want to have multiple independent implementations. 对于应用程序,我建议仅在您想要破坏依赖关系或希望拥有多个独立实现时才使用单独的接口。 If you put the interface and implementation together and need to separate them later, this is a simple refactoring that can be delayed until you need to do it. 如果将接口和实现放在一起并需要稍后将它们分开,这是一个简单的重构,可以延迟直到你需要这样做。

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. 我自己看过这种类型的“花哨”代码,开发人员认为他是SOLID,但却难以理解,难以扩展且过于复杂。

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 ". 它们必须提取接口的原因是它们似乎实现了OOAD原则“ 程序到接口,而不是实现 ”。 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. 是的,这是单元测试的意思。

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. 但它真的没有花太多时间来编写这些接口,如果你需要它们,它们会为你节省很多时间。

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. 当然,你必须计划更多的重用(不需要在以后进行重大的重构)而不是你现在真正要使用的,但为程序中的每个类提取抽象接口意味着你的类比需要。

I like interfaces on things that could be implemented two different ways, either in time or space, ie 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. 我喜欢可以通过两种不同的方式实现的接口,无论是在时间还是空间,即它可以在将来以不同的方式实现,或者在代码的不同部分中有2个不同的代码客户端可能需要不同的实现。

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-- (ie 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). 接口需要维护成本,并且当需要将对象与另一个对象交换时,您可能希望将接口仅应用于少数几个方法(因此更多类可以实现接口),使用抽象可能更好class(以便可以在继承树中实现默认行为)。

So pre-need interfaces is probably not a good idea. 所以预先需要的接口可能不是一个好主意。

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! 随着您的系统不断发展并变得越来越复杂,这一原则越来越有意义!

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. 因此,同一接口总是可以有多个实现。

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 (eg database, filesystem, webservice) and then write a mock or use a mocking framework to simulate the behavior. 我为至少所有接触外部资源的类(例如数据库,文件系统,webservice)创建接口,然后编写模拟或使用模拟框架来模拟行为。

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. 类之间的通信应始终使用接口来完成,除非类彼此紧密绑定。

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

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

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