我应该何时使用接口,何时使用基类?

如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?

如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。


当前回答

通常,您应该喜欢接口而不是抽象类。使用抽象类的一个原因是,如果在具体类之间有共同的实现。当然,您仍然应该声明一个接口(IPet),并让一个抽象类(PetBase)实现该接口。使用小的、不同的接口,您可以使用多个来进一步提高灵活性。接口允许最大限度的灵活性和跨边界类型的可移植性。当跨越边界传递引用时,始终传递接口而不是具体类型。这允许接收端确定具体实施,并提供最大的灵活性。当以TDD/BDD方式编程时,这是绝对正确的。

“四人帮”在他们的书中说:“因为继承将子类暴露于其父类实现的细节,所以人们常说‘继承破坏了封装’。”。我相信这是真的。

其他回答

接口应较小。真的很小。如果你真的要分解你的对象,那么你的接口可能只包含一些非常具体的方法和财产。

抽象类是快捷方式。PetBase的所有衍生产品都有你可以编写一次代码并完成的东西吗?如果是的话,那么是抽象类的时候了。

抽象类也是有限的。虽然它们为您提供了生成子对象的绝佳捷径,但任何给定对象只能实现一个抽象类。很多时候,我发现这是抽象类的局限性,这也是我使用大量接口的原因。

抽象类可以包含多个接口。您的PetBase抽象类可以实现IPet(宠物有主人)和IDigestion(宠物吃,或者至少它们应该吃)。然而,PetBase可能不会实施IMammal,因为并非所有宠物都是哺乳动物,也并非所有哺乳动物都是宠物。您可以添加一个扩展PetBase的MammalPetBase并添加IMammal。FishBase可以有PetBase并添加IFish。IFish将使用ISwim和IUnderwaterBreaker作为接口。

是的,对于简单的示例来说,我的示例非常复杂,但这是接口和抽象类如何协同工作的一部分。

一个重要的区别是,只能继承一个基类,但可以实现多个接口。因此,如果您绝对确定不需要同时继承不同的基类,那么只需要使用基类。此外,如果你发现你的接口越来越大,那么你应该开始把它分成几个逻辑块来定义独立的功能,因为没有规则规定你的类不能实现所有的功能(或者你可以定义一个不同的接口来继承所有的功能)。

接口有一个明显的优点,即对类来说有点“热交换”。将一个类从一个父类更改为另一个父级通常会导致大量工作,但通常可以删除和更改接口,而不会对实现类产生很大影响。这在您有几个“可能”希望类实现的窄行为集的情况下尤其有用。

这在我的领域尤其适用:游戏编程。基类可能会因继承对象“可能”需要的大量行为而变得臃肿。通过接口,可以轻松地向对象添加或删除不同的行为。例如,如果我为想要反映伤害的对象创建了“IDamageEffects”界面,那么我可以很容易地将其应用于各种游戏对象,并在以后很容易地改变主意。假设我设计了一个要用于“静态”装饰对象的初始类,我最初决定它们是不可破坏的。稍后,我可能会决定,如果它们可以爆炸,那会更有趣,所以我修改了类以实现“IDamageEffects”接口。这比切换基类或创建新的对象层次结构要容易得多。

下面是接口和基类的基本和简单定义:

基类=对象继承。接口=功能继承。

干杯

这是非常特定于.NET的,但《框架设计指南》一书认为,一般来说,类在不断发展的框架中提供了更多的灵活性。一旦一个接口交付,您就没有机会在不破坏使用该接口的代码的情况下更改它。然而,对于类,你可以修改它,而不是破坏链接到它的代码。只要你做了正确的修改,包括添加新的功能,你就可以扩展和发展你的代码。

Krzysztof Cwalina在第81页上说:

在.NETFramework的三个版本中,我已经与我们团队中的许多开发人员讨论了这一指南。他们中的许多人,包括那些最初不同意该指南的人,都表示他们后悔将一些API作为接口提供。我甚至没有听说过一个案例,其中有人后悔他们运送了一节课。

也就是说,当然有接口的地方。作为一般准则,始终提供接口的抽象基类实现,作为实现接口的方法的示例。在最佳情况下,基类将节省大量工作。