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

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

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


当前回答

感谢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();
}

其他回答

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

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

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

如果除了您的类型成员之外,其他开发人员确实没有任何理由希望使用自己的基类,并且您预见到版本控制问题,那么您应该使用基类(请参见http://haacked.com/archive/2008/02/21/versioning-issues-with-abstract-base-classes-and-interfaces.aspx).

如果继承开发人员有任何理由使用自己的基类来实现类型的接口,并且您没有看到接口发生变化,那么就使用接口。在这种情况下,为了方便起见,您仍然可以加入实现接口的默认基类。

接口

大多数语言允许您实现多个接口修改接口是一项突破性的更改。所有实现都需要重新编译/修改。所有成员都是公共的。实现必须实现所有成员。接口有助于解耦。您可以使用模拟框架模拟接口后面的任何内容接口通常表示一种行为接口实现彼此分离/隔离

基类

允许您添加一些通过派生免费获得的默认实现(从C#8.0通过接口可以获得默认实现)除了C++,您只能从一个类派生。即使可以从多个类中选择,这通常也是一个坏主意。更改基类相对容易。派生不需要做任何特殊的事情基类可以声明可由派生访问的受保护函数和公共函数抽象基类不能像接口那样容易被模仿基类通常表示类型层次结构(IS A)类派生可能会依赖于一些基本行为(具有复杂的父实现知识)。如果你对一个人的基本实现进行更改并打破其他人,事情可能会很混乱。

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

通过def,接口提供了一个与其他代码通信的层。默认情况下,类的所有公共财产和方法都实现隐式接口。我们也可以将接口定义为一个角色,当任何类都需要扮演这个角色时,它必须实现它,根据实现它的类,给它不同的实现形式。oops的两个概念!!!