我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现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问题:
我应该如何解释接口和抽象类之间的区别?
其他回答
在Submain.NET编码指南中很好地解释了接口上基类的情况:
基类与接口接口类型是部分值的描述,可能受许多对象类型支持。使用基类而不是接口只要可能。从版本控制透视,类更灵活而不是接口。有了课,你可以发布版本1.0,然后发布版本2.0向类中添加新方法。只要该方法不是抽象的,任何现有派生类都将继续功能不变。因为接口不支持实现继承应用于类的模式不适用于接口。添加方法与接口等效向基中添加抽象方法班实现接口将中断,因为类不实现新方法。接口适用于以下情况:几个不相关的类希望支持该协议。这些类已经建立了基类(对于实例一些是用户界面(UI)控件,一些是XMLWeb服务)。聚合不合适或不可行。在所有其他情况,类继承是更好的模型。
关于C#,在某些意义上,接口和抽象类可以互换。然而,区别在于:i)接口不能实现代码;ii)由于这一点,接口不能进一步调用堆栈上的子类;以及iii)抽象类只能在类上继承,而多个接口可以在类上实现。
感谢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();
}
让我们以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++),但能够实现多个接口,因此它允许您严格按照需要构造对象。
接口
大多数语言允许您实现多个接口修改接口是一项突破性的更改。所有实现都需要重新编译/修改。所有成员都是公共的。实现必须实现所有成员。接口有助于解耦。您可以使用模拟框架模拟接口后面的任何内容接口通常表示一种行为接口实现彼此分离/隔离
基类
允许您添加一些通过派生免费获得的默认实现(从C#8.0通过接口可以获得默认实现)除了C++,您只能从一个类派生。即使可以从多个类中选择,这通常也是一个坏主意。更改基类相对容易。派生不需要做任何特殊的事情基类可以声明可由派生访问的受保护函数和公共函数抽象基类不能像接口那样容易被模仿基类通常表示类型层次结构(IS A)类派生可能会依赖于一些基本行为(具有复杂的父实现知识)。如果你对一个人的基本实现进行更改并打破其他人,事情可能会很混乱。