协慌网

登录 贡献 社区

接口与基类

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

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

如果我有狗和猫类。为什么我要实现 IPet 而不是 PetBase?我可以理解有 ISheds 或 IBarks(IMakesNoise?)的接口,因为那些可以基于宠物放在宠物上,但我不明白哪个用于通用 Pet。

答案

让我们以你的 Dog 和 Cat 类为例,让我们用 C#来说明:

狗和猫都是动物,特别是四足动物的哺乳动物(动物过于笼统)。让我们假设你有两个抽象类 Mammal:

public abstract class Mammal

这个基类可能有默认方法,例如:

  • 饲料
  • 伴侣

所有这些都是在两个物种之间具有或多或少相同实施的行为。要定义它,您将拥有:

public class Dog : Mammal
public class Cat : Mammal

现在让我们假设还有其他哺乳动物,我们通常会在动物园看到它们:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

这仍然有效,因为功能的核心Feed()Mate()仍然是相同的。

然而,长颈鹿,犀牛和河马并不是你可以养宠物的动物。这就是界面有用的地方:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

上述合同的实施在猫与狗之间是不一样的; 将他们的实现放在一个抽象类中继承将是一个坏主意。

您的狗和猫定义现在应该如下所示:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

从理论上讲,您可以从更高的基类覆盖它们,但实际上,一个接口允许您只需要将所需的内容添加到类中而无需继承。

因此,因为您通常只能从一个抽象类继承(在大多数静态类型的 OO 语言中,例外包括 C ++)但能够实现多个接口,它允许您严格按要求构造对象。

好吧,Josh Bloch 在Effective Java 2d 中说自己:

首选接口而不是抽象类

一些要点:

  • 可以轻松地对现有类进行改装以实现新接口 。您所要做的就是添加所需的方法(如果它们尚不存在)并将一个 implements 子句添加到类声明中。

  • 接口是定义 mixins 的理想选择 。松散地说,mixin 是类可以实现的类型,除了它的 “主类型” 之外,它声明它提供了一些可选行为。例如,Comparable 是一个 mixin 接口,允许类声明其实例相对于其他可相互比较的对象进行排序。

  • 接口允许构造非分层类型框架 。类型层次结构非常适合组织某些事物,但其他事情并不能完全落入严格的层次结构中。

  • 接口通过包装类习惯用法实现安全,强大的功能增强 。如果您使用抽象类来定义类型,那么您可以让想要添加功能的程序员别无选择,只能使用继承。

此外,您可以通过提供抽象骨架实现类来组合接口和抽象类的优点,以与您导出的每个非平凡接口一起使用。

另一方面,接口很难发展。如果向接口添加方法,它将破坏它的所有实现。

PS:买这本书。它更加详细。

现代风格是定义 IPet PetBase。

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

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