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

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

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


当前回答

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

其他回答

从概念上讲,接口用于正式和半正式地定义对象将提供的一组方法。形式上表示一组方法名称和签名,半形式上表示与这些方法相关的人类可读文档。

接口只是API的描述(毕竟,API代表应用程序编程接口),它们不能包含任何实现,也不可能使用或运行接口。它们只明确约定你应该如何与对象交互。

类提供了一个实现,它们可以声明它们实现了零个、一个或多个接口。如果要继承类,则惯例是在类名前面加上“Base”。

基类和抽象基类(ABC)之间有区别。ABC将接口和实现混合在一起。计算机编程之外的抽象意味着“摘要”,即“抽象==接口”。然后抽象基类可以描述接口,也可以描述要继承的空的、部分的或完整的实现。

关于何时使用接口、抽象基类还是仅使用类的意见将根据您正在开发的内容以及您正在使用的语言而大不相同。接口通常仅与静态类型语言(如Java或C#)相关,但动态类型语言也可以有接口和抽象基类。例如,在Python中,Class和对象之间的区别很明显,前者声明它实现了一个接口,后者是一个类的实例,据说提供了该接口。在动态语言中,两个都是同一类实例的对象可以声明它们提供完全不同的接口。在Python中,这仅适用于对象属性,而方法在类的所有对象之间共享状态。然而,在Ruby中,对象可以具有每实例方法,因此同一类的两个对象之间的接口可能会根据程序员的需要而变化(然而,Ruby没有任何明确的接口声明方式)。

在动态语言中,对象的接口通常是隐式假设的,可以通过内省对象并询问它提供了什么方法(在跳转之前查看),也可以通过简单地尝试在对象上使用所需的接口并在对象不提供该接口时捕捉异常(请求原谅比请求许可更容易)。这可能导致“误报”,即两个接口具有相同的方法名称,但语义不同。然而,权衡是代码更加灵活,因为您不需要预先过度指定以预测代码的所有可能用途。

我通常在需要之前都不会实现。与抽象类相比,我更喜欢接口,因为这提供了更多的灵活性。如果在某些继承类中有共同的行为,我会将其上移并创建一个抽象基类。我不认为两者都有必要,因为它们本质上服务于相同的目的,而且两者都有一种糟糕的代码气味(imho),解决方案被过度设计了。

让我们以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++),但能够实现多个接口,因此它允许您严格按照需要构造对象。

首选接口而非抽象类

根本原因需要考虑的要点[这里已经提到了两个]是:

接口更灵活,因为一个类可以实现多个接口。由于Java没有多重继承,因此使用抽象类防止用户使用任何其他类等级制度通常,当没有默认值时,首选接口实现或状态。Java集合提供了这(地图、集合等)。抽象类的优点是允许更好的转发兼容性。一旦客户端使用了一个接口,就不能更改它;如果它们使用抽象类,您仍然可以添加行为而不使用破坏现有代码。如果兼容性是一个问题,请考虑使用抽象类。即使您有默认实现或内部状态,考虑提供一个接口及其抽象实现。这将有助于客户,但如果所需[1]。当然,这个问题已经讨论了很长时间其他地方[2,3]。

[1] 当然,它增加了更多的代码,但如果简洁是您的主要关注点,那么您可能应该首先避免使用Java!

[2] Joshua Bloch,《有效的Java》,第16-18项。

[3] http://www.codeproject.com/KB/ar...

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