接口代表契约的答案是不可接受的。
这就是我们给Junior的答案,因为如果没有太多的架构经验,也没有读过很多经典书籍,就很难清楚地弄清楚抽象类的本质和接口的本质之间的区别,这可能太复杂了。
任何抽象类public
方法既充当契约,又充当接口。
不提供任何实现的抽象类在 99% 的情况下是对象的表示Role.
An 界面代表一个Role.
每个对象可能具有多个不同的角色,这些角色不应捆绑在一起,而应由相关对象组成。
我用这个例子来解释这一点:
你的面试官可能会说:
我有一个Robot
可以行走并且Human
那也可以走路。
因此,基于这个案例,他问你:在知道实现没有任何共同点的情况下,我应该将步行功能提取到抽象基类中还是接口中?
你认为......“哦,我知道是这样:在这种情况下,有一个带有抽象方法的抽象类walk()
,那么显然与声明一个接口相同walk()
方法。”
所以你的答案肯定是:“这是开发商的选择!”。
这确实不是一个始终有效的答案。
为什么?让我们看看下一个期望:
A Human
可以吃,但显然Robot
不能甚至不需要。
如果您使用抽象类实现行走功能会怎样?你最终会得到:
public abstract class Biped {
public void abstract walk();
}
public Robot extends Biped {
public void walk() {
//walk at 10km/h speed
}
}
public Human extends Biped {
public void walk() {
//walk at 5km/h speed
}
}
你怎么能插上eating
特征?你被困住了,因为你无法在Biped
基类,因为它会破坏里氏替换原则,由于Robot
不吃!
而你无法期望Human
由于已知的 Java 规则,扩展了另一个基类。
当然,您可以添加专门用于人类的特定 Feedable 接口:
public interface Feedable {
void eat();
}
签名变为:public Human extends Biped implements Feedable {
显然,拥有一个没有任何意义并且令人困惑role一个通过类实现,另一个通过接口实现。
这就是为什么只要我们有选择,从界面开始确实是首选。
通过接口,我们可以建模Roles轻松地通过作曲。
所以最终的解决方案是:
public interface Walkable {
void abstract walk();
}
public interface Feedable {
void eat();
}
public Robot implements Walkable {
public void walk() {
//walk at 10km/h speed
}
}
public Human implements Walkable, Feedable {
public void walk() {
//walk at 5km/h speed
}
public void eat(){
//...
}
}
难道它没有提醒你接口隔离原则? ;)
总而言之,如果您指定IS-A关系,使用抽象类。
如果您意识到您要建模Role(假设一个有能力关系),与接口一起走。