我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
当前回答
我应该何时使用接口,何时使用基类?
如果
你有纯抽象方法,没有非抽象方法您没有非抽象方法的默认实现(除了Java 8语言,其中接口方法提供默认实现)若您使用的是Java8,现在接口将为一些非抽象方法提供默认实现。与抽象类相比,这将使接口更可用。
请查看此SE问题以了解更多详细信息。
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
对它更好更干净。即使您有一个带有一些抽象方法的基类,让我们的基类通过接口扩展抽象方法。您可以在将来更改接口而不更改基类。
java示例:
abstract class PetBase implements IPet {
// Add all abstract methods in IPet interface and keep base class clean.
Base class will contain only non abstract methods and static methods.
}
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
我更希望基类实现接口。
abstract class PetBase implements IPet {
// Add all abstract methods in IPet
}
/*If ISheds,IBarks is common for Pets, your PetBase can implement ISheds,IBarks.
Respective implementations of PetBase can change the behaviour in their concrete classes*/
abstract class PetBase implements IPet,ISheds,IBarks {
// Add all abstract methods in respective interfaces
}
优势:
如果我想在现有接口中再添加一个抽象方法,我只需简单地更改接口,而不需要接触抽象基类。如果我想更改契约,我将在不接触基类的情况下更改接口和实现类。您可以通过接口为基类提供不变性。看看这篇文章
有关详细信息,请参阅此相关SE问题:
我应该如何解释接口和抽象类之间的区别?
其他回答
我通常在需要之前都不会实现。与抽象类相比,我更喜欢接口,因为这提供了更多的灵活性。如果在某些继承类中有共同的行为,我会将其上移并创建一个抽象基类。我不认为两者都有必要,因为它们本质上服务于相同的目的,而且两者都有一种糟糕的代码气味(imho),解决方案被过度设计了。
我有一个粗略的经验法则
功能:可能在所有部分都不同:界面。
数据和功能,部分基本相同,部分不同:抽象类。
数据和功能,实际工作,如果只进行了轻微的修改:普通(具体)类
数据和功能,没有计划的更改:带有最终修饰符的普通(具体)类。
数据,可能还有功能:只读:枚举成员。
这是非常粗略和准备好的,并且根本没有严格定义,但是有一个范围是从所有内容都要更改的接口到所有内容都像只读文件一样固定的enums。
我建议尽可能使用组合而不是继承。使用接口,但对基本实现使用成员对象。这样,您可以定义一个工厂,该工厂构造对象以某种方式运行。如果要更改行为,则创建一个新的工厂方法(或抽象工厂),以创建不同类型的子对象。
在某些情况下,如果所有可变行为都在帮助对象中定义,那么您可能会发现主对象根本不需要接口。
因此,您可能会得到一个带有IFurBehavior参数的宠物,而不是IPet或PetBase。IFurBehavior参数由PetFactory的CreateDog()方法设置。shed()方法调用的就是这个参数。
如果你这样做,你会发现你的代码更加灵活,你的大多数简单对象都处理非常基本的系统范围行为。
即使在多种继承语言中,我也推荐这种模式。
感谢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();
}
接口应较小。真的很小。如果你真的要分解你的对象,那么你的接口可能只包含一些非常具体的方法和财产。
抽象类是快捷方式。PetBase的所有衍生产品都有你可以编写一次代码并完成的东西吗?如果是的话,那么是抽象类的时候了。
抽象类也是有限的。虽然它们为您提供了生成子对象的绝佳捷径,但任何给定对象只能实现一个抽象类。很多时候,我发现这是抽象类的局限性,这也是我使用大量接口的原因。
抽象类可以包含多个接口。您的PetBase抽象类可以实现IPet(宠物有主人)和IDigestion(宠物吃,或者至少它们应该吃)。然而,PetBase可能不会实施IMammal,因为并非所有宠物都是哺乳动物,也并非所有哺乳动物都是宠物。您可以添加一个扩展PetBase的MammalPetBase并添加IMammal。FishBase可以有PetBase并添加IFish。IFish将使用ISwim和IUnderwaterBreaker作为接口。
是的,对于简单的示例来说,我的示例非常复杂,但这是接口和抽象类如何协同工作的一部分。