问题描述
虽然某些指导原则规定,如果要为继承不明确的类( IDomesticated
)定义合同,并且类为继承时,应使用接口。另一个( Cat:Mammal
, Snake:Reptile
)的延伸,有些情况(在我看来)这些准则输入灰色区域。
While certain guidelines state that you should use an interface when you want to define a contract for a class where inheritance is not clear (IDomesticated
) and inheritance when the class is an extension of another (Cat : Mammal
, Snake : Reptile
), there are cases when (in my opinion) these guidelines enter a gray area.
例如,假设我的实现是 Cat:Pet
。 Pet
是一个抽象类。应该扩展到 Cat:Mammal,IDomesticated
其中 Mammal
是一个抽象类, IDomesticated
是一个界面?或者我与 / 原则(即使我不确定是否会有一个 Wolf
类未来,它将无法继承 Pet
)?
For example, say my implementation was Cat : Pet
. Pet
is an abstract class. Should that be expanded to Cat : Mammal, IDomesticated
where Mammal
is an abstract class and IDomesticated
is an interface? Or am I in conflict with the KISS/YAGNI principles (even though I'm not sure whether there will be a Wolf
class in the future, which would not be able to inherit from Pet
)?
远离隐喻 Cat
s和 Pet
s,假设我有一些表示传入数据源的类。他们都需要以某种方式实现相同的基础。我可以在抽象的 Source
类中实现一些通用代码并从中继承。我还可以创建一个 ISource
接口(对我来说感觉更正确)并在每个类中重新实现通用代码(这不太直观)。最后,通过制作抽象类和界面,我可以吃蛋糕并吃掉它。什么是最好的?
Moving away from the metaphorical Cat
s and Pet
s, let's say I have some classes that represent sources for incoming data. They all need to implement the same base somehow. I could implement some generic code in an abstract Source
class and inherit from it. I could also just make an ISource
interface (which feels more "right" to me) and re-implement the generic code in each class (which is less intuitive). Finally, I could "have the cake and eat it" by making both the abstract class and the interface. What's best?
这两种情况提出了仅使用抽象类,只使用接口并同时使用抽象类和接口的要点。这些都是有效的选择,或者是否有规则用于何时应该使用另一个?
These two cases bring up points for using only an abstract class, only an interface and using both an abstract class and an interface. Are these all valid choices, or are there "rules" for when one should be used over another?
我是喜欢澄清通过使用抽象类和接口,包括它们基本上代表同一事物的情况( Source
和 ISource
都有相同的成员),但是该类在接口指定合同时添加了通用功能。
I'd like to clarify that by "using both an abstract class and an interface" that includes the case when they essentially represent the same thing (Source
and ISource
both have the same members), but the class adds generic functionality while the interface specifies the contract.
另外值得注意的是,这个问题主要是对于不支持多重继承的语言(例如.NET和Java)。
Also worth noting is that this question is mostly for languages that do not support multiple inheritance (such as .NET and Java).
推荐答案
作为第一条经验法则,我更喜欢接口上的抽象类,。这个推理比.NET更广泛,但在框架设计指南。
As a first rule of thumb, I prefer abstract classes over interfaces, based on the .NET Design Guidelines. The reasoning applies much wider than .NET, but is better explained in the book Framework Design Guidelines.
抽象基类的首选背后的主要原因是版本控制,因为您总是可以在不破坏现有客户端的情况下将新的虚拟成员添加到抽象基类。接口是不可能的。
The main reasoning behind the preference for abstract base classes is versioning, because you can always add a new virtual member to an abstract base class without breaking existing clients. That's not possible with interfaces.
有些情况下接口仍然是正确的选择(特别是当你不关心版本控制时),但是要意识到它们的优点和缺点使您能够做出正确的决定。
There are scenarios where an interface is still the correct choice (particularly when you don't care about versioning), but being aware of the advantages and disadvantages enables you to make the correct decision.
所以在我继续之前作为部分答案:如果您决定编码,只有接口和基类才有意义首先是对接口。如果允许接口,则必须仅针对该接口进行编码,否则您将违反Liskov替换原则。换句话说,即使您提供了实现接口的基类,也不能让您的代码使用该基类。
So as a partial answer before I continue: Having both an interface and a base class only makes sense if you decide to code against an interface in the first place. If you allow an interface, you must code against that interface only, since otherwise you would be violating the Liskov Substitution Principle. In other words, even if you provide a base class that implements the interface, you cannot let your code consume that base class.
如果您决定对基类进行编码具有接口是没有意义的。
If you decide to code against a base class, having an interface makes no sense.
如果您决定对接口进行编码,则具有提供默认功能的基类是可选的。这没有必要,但可以为实施者加快速度,所以你可以提供一个礼貌。
If you decide to code against an interface, having a base class that provides default functionality is optional. It is not necessary, but may speed up things for implementers, so you can provide one as a courtesy.
一个让人想起的例子就是ASP.NET MVC。请求管道在IController上工作,但是你通常使用一个Controller基类来实现行为。
An example that springs to mind is in ASP.NET MVC. The request pipeline works on IController, but there's a Controller base class that you typically use to implement behavior.
最终答案:如果使用抽象基类,只使用它。如果使用接口,基类是实施者可选的礼貌。
Final answer: If using an abstract base class, use only that. If using an interface, a base class is an optional courtesy to implementers.
更新:我不再喜欢接口上的抽象类,我已经很久没有了;相反,我倾向于使用SOLID作为指导而不是继承。
Update: I no longer prefer abstract classes over interfaces, and I haven't for a long time; instead, I favour composition over inheritance, using SOLID as a guideline.
(虽然我可以直接编辑上面的文字,但它会从根本上改变帖子的性质,并且因为有些人发现它足够有价值来投票,所以我宁愿让原始文本站起来,而是添加这个注释。帖子的后半部分仍然有意义,所以删除它会是一种耻辱也是。)
(While I could edit the above text directly, it would radically change the nature of the post, and since a few people have found it valuable enough to up-vote it, I'd rather let the original text stand, and instead add this note. The latter part of the post is still meaningful, so it would be a shame to delete it, too.)
这篇关于何时使用接口或抽象类?何时使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!