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

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

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


当前回答

不要使用基类,除非您知道它的含义,并且它适用于这种情况。如果适用,使用它,否则使用接口。但请注意关于小接口的答案。

公共继承在OOD中被过度使用,表达的内容比大多数开发人员意识到或愿意实现的要多得多

简而言之,如果A“是一个”B,那么对于它公开的每个方法,A需要的不超过B,交付的不少于B。

其他回答

我建议尽可能使用组合而不是继承。使用接口,但对基本实现使用成员对象。这样,您可以定义一个工厂,该工厂构造对象以某种方式运行。如果要更改行为,则创建一个新的工厂方法(或抽象工厂),以创建不同类型的子对象。

在某些情况下,如果所有可变行为都在帮助对象中定义,那么您可能会发现主对象根本不需要接口。

因此,您可能会得到一个带有IFurBehavior参数的宠物,而不是IPet或PetBase。IFurBehavior参数由PetFactory的CreateDog()方法设置。shed()方法调用的就是这个参数。

如果你这样做,你会发现你的代码更加灵活,你的大多数简单对象都处理非常基本的系统范围行为。

即使在多种继承语言中,我也推荐这种模式。

在Submain.NET编码指南中很好地解释了接口上基类的情况:

基类与接口接口类型是部分值的描述,可能受许多对象类型支持。使用基类而不是接口只要可能。从版本控制透视,类更灵活而不是接口。有了课,你可以发布版本1.0,然后发布版本2.0向类中添加新方法。只要该方法不是抽象的,任何现有派生类都将继续功能不变。因为接口不支持实现继承应用于类的模式不适用于接口。添加方法与接口等效向基中添加抽象方法班实现接口将中断,因为类不实现新方法。接口适用于以下情况:几个不相关的类希望支持该协议。这些类已经建立了基类(对于实例一些是用户界面(UI)控件,一些是XMLWeb服务)。聚合不合适或不可行。在所有其他情况,类继承是更好的模型。

现代风格是定义IPet和PetBase。

该接口的优点是其他代码可以使用它,而与其他可执行代码没有任何联系。完全“干净”。界面也可以混合。

但是基类对于简单的实现和通用实用程序很有用。因此,还提供一个抽象基类,以节省时间和代码。

这取决于您的要求。如果IPet足够简单,我更愿意实现它。否则,如果PetBase实现了大量您不想复制的功能,那么就试试看。

实现基类的缺点是需要重写(或新的)现有方法。这使它们成为虚拟方法,这意味着您必须小心如何使用对象实例。

最后,.NET的单一继承让我很头疼。一个天真的例子:假设你正在创建一个用户控件,那么你就继承了UserControl。但是,现在你被禁止继承PetBase。这迫使您重新组织,例如创建PetBase类成员。

感谢Jon Limjap的回答,但我想为接口和抽象基类的概念添加一些解释

接口类型与抽象基类

改编自Pro C#5.0和.NET 4.5 Framework书籍。

接口类型似乎与抽象基类非常相似。回忆起当类被标记为抽象时,它可以定义任意数量的抽象成员来提供所有派生类型的多态接口。然而,即使类确实定义了一组抽象成员,还可以自由定义任意数量的构造函数、字段数据、非抽象成员(使用另一方面,接口只包含抽象成员定义。抽象父类建立的多态接口有一个主要限制因为只有派生类型支持抽象父级定义的成员。然而,在更大范围内软件系统,开发多个没有共同父级的类层次结构是非常常见的超出System.Object。假定抽象基类中的抽象成员仅适用于派生类型,我们无法在不同的层次结构中配置类型以支持相同的多态性界面例如,假设您定义了以下抽象类:

public abstract class CloneableType
{
// Only derived types can support this
// "polymorphic interface." Classes in other
// hierarchies have no access to this abstract
// member.
   public abstract object Clone();
}

给定此定义,只有扩展CloneableType的成员才能支持Clone()方法如果创建一组不扩展此基类的新类,则无法获得多态界面。此外,您可能还记得C#不支持类的多重继承。因此,如果您想创建一辆小型货车,它是一辆汽车,并且是一种可克隆类型,那么您无法做到:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}

正如您所猜测的,接口类型起到了拯救作用。定义接口后,可以可以由任何类或结构、任何层次结构、任何命名空间或任何程序集中实现(用任何.NET编程语言编写)。正如您所看到的,接口是高度多态的。考虑System命名空间中定义的名为ICloneable的标准.NET接口。这接口定义了一个名为Clone()的方法:

public interface ICloneable
{
object Clone();
}