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

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

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


当前回答

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

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

其他回答

首选接口而非抽象类

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

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

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

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

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

接口和基类表示两种不同形式的关系。

继承(基类)表示“is-a”关系。例如,狗或猫是宠物。这种关系始终代表着班级的(单一)目的(结合“单一责任原则”)。

另一方面,接口表示类的其他特性。我将其称为“是”关系,如“Foo是一次性的”,因此C#中的IDisposable接口。

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

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

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

我有一个粗略的经验法则

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

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

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

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

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

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

我发现Interface>Abstract>Concrete的模式适用于以下用例:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

抽象类定义了具体类的默认共享属性,但实施了接口。例如:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

现在,由于所有的哺乳动物都有毛发和乳头(AFAIK,我不是动物学家),我们可以将其纳入抽象基类

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

然后具体的类仅仅定义了他们直立行走。

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

当有很多具体的类时,这种设计是很好的,并且您不希望维护样板只是为了编程到接口。如果向接口中添加了新方法,则会破坏所有生成的类,因此您仍然可以获得接口方法的优势。

在这种情况下,抽象也可以是具体的;然而,抽象的名称有助于强调这种模式正在被使用。