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

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

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


当前回答

关于C#,在某些意义上,接口和抽象类可以互换。然而,区别在于:i)接口不能实现代码;ii)由于这一点,接口不能进一步调用堆栈上的子类;以及iii)抽象类只能在类上继承,而多个接口可以在类上实现。

其他回答

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

继承还有其他优点,例如变量能够保存父类或继承类的对象(无需将其声明为泛型,如“object”)。

例如,在.NET WinForms中,大多数UI组件都是从System.Windows.Forms.Control派生的,因此声明为的变量可以“容纳”几乎所有UI元素,无论是按钮、ListView还是其他元素。当然,现在你不能访问项目的所有财产或方法,但你可以拥有所有基本的东西——这可能很有用。

还请记住,不要在OO中被卷走(请参见博客),并始终基于所需的行为来建模对象,如果您设计的应用程序中唯一需要的行为是动物的通用名称和物种,那么您只需要一个类animal,而不是世界上每种可能的动物都需要数百万个类。

胡安,

我喜欢将接口视为一种描述类的方式。一个特定的犬类,比如约克郡梗,可能是母犬类的后代,但它也执行IFurry、IStubby和IYippieDog。所以类定义了类是什么,但接口告诉了我们关于它的事情。

这样做的好处是,例如,它允许我收集所有的IYippieDog,并将它们放入我的海洋收藏。因此,现在我可以跨越一组特定的对象,找到符合我所关注的标准的对象,而不必过于仔细地检查类。

我发现接口确实应该定义类的公共行为的子集。如果它为实现的所有类定义了所有公共行为,那么它通常不需要存在。他们没有告诉我任何有用的东西。

但这种想法与每个类都应该有一个接口的想法背道而驰,您应该编写接口代码。这很好,但最终会有很多一对一的类接口,这会让事情变得混乱。我知道这样做并不需要花费任何费用,现在你可以轻松地交换东西。然而,我发现我很少这样做。大多数时候,我只是在原地修改现有的类,如果该类的公共接口需要更改,我会遇到完全相同的问题,但我现在必须在两个地方更改它。

所以,如果你像我一样思考,你肯定会说《猫和狗》是IPettable的。这是一个符合两者的特征。

但另一个问题是,它们是否应该有相同的基类?问题是,它们是否需要被广泛地视为同一事物。当然,它们都是动物,但这是否符合我们将如何一起使用它们。

假设我想收集所有动物类并将它们放在我的方舟容器中。

或者它们需要是哺乳动物吗?也许我们需要某种跨动物挤奶工厂?

他们甚至需要联系在一起吗?仅仅知道它们都是IPettable就足够了吗?

当我真的只需要一个类时,我常常会感到想要派生出一个完整的类层次结构。我做这件事是为了期待有一天我可能会需要它,但通常我从来不会这样做。即使我做了,我通常也会发现我必须做很多事情来修复它。这是因为我创建的第一个类不是狗,我没有那么幸运,而是鸭嘴兽。现在,我的整个类层次结构都基于这个奇怪的案例,我有很多浪费的代码。

你可能还会发现,并不是所有的猫都是IPettable的(比如那个无毛的猫)。现在您可以将该接口移动到所有适合的派生类。你会发现,突然之间,猫不再是PettableBase的产物,这是一个小得多的变化。

我有一个粗略的经验法则

功能:可能在所有部分都不同:界面。

数据和功能,部分基本相同,部分不同:抽象类。

数据和功能,实际工作,如果只进行了轻微的修改:普通(具体)类

数据和功能,没有计划的更改:带有最终修饰符的普通(具体)类。

数据,可能还有功能:只读:枚举成员。

这是非常粗略和准备好的,并且根本没有严格定义,但是有一个范围是从所有内容都要更改的接口到所有内容都像只读文件一样固定的enums。