到目前为止,所有的答案都回答的是:什么是抽象类,什么是接口。也就是从概念上来区分它们。
我的答案,想尝试回答:什么时候使用它们。
当前类已继承了一个class,则interface是唯一选择。
这是java单继承的语言特性,对别的语言并不适用。
if !(非abstract class不可) {
interface
}
为什么优先interface?因为它最简单。class有可能涉及到数据(member variable),或许牵涉到具体算法(method)。能不用就不用。
程序就是要as simple as possible。
综上,只要知道何时用class,其余情况均用interface。
何时用class?我想跳过那些无用的概念,讲讲实际是怎么操作的。
********************************举个栗子********************************
比如说一个线上下单,最基本的功能有三个:支付
修改库存
发货
最开始支付采用的是Alipay,一个class搞定。
public class Item {
public boolean buy() {
if(pay()) {
decrementStock();
deliver();
return true;
}
return false;
}
private boolean pay() {
// 调用alipay的接口 }
private void decrementStock() {
// .... }
private void deliver() {
// .... }
}
后来需要对接WeixinPay,最容易想到的是,在pay()里加入微信支付的逻辑。
这种方式看上去不错,却有两个缺点:为了区分支付渠道,我们需要加入一个渠道标识符。(是增加一个member variable?还是从buy方法开始,就增加一个function variable?)
后续要增加支付渠道,还得一遍遍修改pay方法。方法的本身体积膨胀不说,牵一发动全身,引出的风险也不小。
拥抱变化,同时降低风险的做法是新增加类:
public abstract class Item {
public boolean buy() {
if(pay()) {
decrementStock();
deliver();
return true;
}
return false;
}
protected abstract boolean pay() {
}
protected void decrementStock() {
// .... }
protected void deliver() {
// .... }
}
public class ItemByAliPay extends Item {
@Override
protected boolean pay() {
// 调用alipay的接口 }
}
public class ItemByWechat extends Item {
@Override
protected boolean pay() {
// 调用weixinPay的接口 }
}
回过来看新写法的改动:Item类加上abstract
=>这是因为我们不再使用Item来实例化对象。最后new的,一定是其子类。
2. Item类的private方法变为protected
=>这是因为我们希望在子类中,也能使用到这些方法。
3. Item类的Pay方法删掉了具体实现,加上abstract,演变为抽象类方法
=>父类的方法加上abstract,代表将可变部分的逻辑,交由子类实现。
最后来看Item类的buy方法
=>decrementStock和deliver是通用逻辑,供子类使用;
=>pay是抽象方法,由子类具体实现;
=>整个buy方法,负责业务流程的控制;
********************************划重点********************************
类,也包含抽象类,是:
①编写通用逻辑供子类使用;
②负责业务流程的控制;
回答什么时候使用抽象类,最重要的就是这第②点。
如果只有①,抽象类可以退化成Utils工具类。
如果①和②都不需要,则对可变的pay方法来说,转化成interface是更好的选择。
以上