这里有一些很棒的回答,涉及到接口、松耦合代码、控制反转等方面的细节。但是,有些讨论比较深奥,我想利用这个机会为大家简单解释一下为什么接口很有用。
当我第一次接触接口时,我也很困惑它们的相关性。我不明白为什么需要它们。如果我们使用像Java或C#这样的语言,我们已经有了继承,我认为接口只是继承的一种弱化表现形式。那么,为什么要费劲去实现它呢?从某种程度上说,我的观点没错,你可以把接口看作是继承的一种弱化表现形式,但除此之外,我终于明白了它们作为一种语言构造的用处,即通过将许多非相关类对象所共有的特点或行为分类来使用它们。
例如——假设你有一个SIM游戏,有以下几个类:
class HouseFly inherits Insect {
void FlyAroundYourHead(){}
void LandOnThings(){}
}
class Telemarketer inherits Person {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
}
显然,这两个对象在直接继承方面没有任何共同之处。但是,你可以说它们都很烦人。
假设我们的游戏需要有某种随机“东西”,当玩家吃饭时它会让他们感到烦恼。这可能是一只HouseFly
或一个Telemarketer
,或者两者都有,但是如何使用单个函数允许两者存在呢?并且如何要求每个不同类型的对象以相同的方式“做出它们的烦人之事”呢?
关键在于意识到,Telemarketer
和HouseFly
虽然在建模上没有任何相似之处,但它们共享一种常见的松散解释行为。因此,让我们创建一个接口,让两者都可以实现:
interface IPest {
void BeAnnoying();
}
class HouseFly inherits Insect implements IPest {
void FlyAroundYourHead(){}
void LandOnThings(){}
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person implements IPest {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
现在我们有两个类,它们各自都可以以自己的方式变得令人讨厌。它们不需要从同一基类派生并共享共同本质特征-它们只需要满足IPest
的合约-这个合约很简单。你只需要 BeAnnoying
。在这方面,我们可以建模如下:
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
这里有一个餐厅,可以容纳很多食客和害虫。请注意接口的使用。这意味着在我们的小世界中,pests
数组的成员实际上可以是 Telemarketer
对象或 HouseFly
对象。
ServeDinner
方法在用餐时被调用,我们在餐厅的人应该吃东西。在我们的小游戏中,这时我们的害虫会开始工作——每只害虫都会通过 IPest
接口被指示如何令人讨厌。通过这种方式,我们可以轻松地让 Telemarketers
和 HouseFlys
以各自不同的方式让人讨厌,而我们只关心在 DiningRoom
对象中有一个害虫,真正的对象类型并不重要,它们彼此之间可能没有任何共同点。
这个非常牵强附会的伪代码示例(比我预期的要冗长得多)只是为了说明我们何时可以使用接口。我提前为这个示例的愚蠢表示歉意,但是希望它能有助于您的理解。当然,您在这里收到的其他回答已经涵盖了接口在设计模式和开发方法中使用的方方面面。