选择Java接口还是抽象类
作者俞良松 本文选自开放系统世界-赛迪网 年月日
很多人有过这样的疑问为什么有的地方必须使用接口而不是抽象类而在另一些地方又必须使用抽象类而不是接口呢?或者说在考虑Java类的一般化问题时很多人会在接口和抽象类之间犹豫不决甚至随便选择一种
实际上接口和抽象类的选择不是随心所欲的 要理解接口和抽象类的选择原则有两个概念很重要对象的行为和对象的实现如果一个实体可以有多种实现方式则在设计实体行为的描述方式时应当达到这样一个目标在使用实体的时候无需详细了解实体行为的实现方式也就是说要把对象的行为和对象的实现分离开来既然Java的接口和抽象类都可以定义不提供具体实现的方法在分离对象的行为和对象的实现时到底应该使用接口还是使用抽象类呢?
通过抽象类建立行为模型
在接口和抽象类的选择上必须遵守这样一个原则行为模型应该总是通过接口而不是抽象类定义为了说明其原因下面试着通过抽象类建立行为模型看看会出现什么问题
假设要为销售部门设计一个软件这个软件包含一个发动机(Motor)实体显然无法在发动机对象中详细地描述发动机的方方面面只能描述某些对当前软件来说重要的特征至于发动机的哪些特征是重要的则要与用户(销售部门)交流才能确定
销售部门的人要求每一个发动机都有一个称为马力的参数对于他们来说这是惟一值得关心的参数基于这一判断可以把发动机的行为定义为以下行为
行为查询发动机的马力发动机将返回一个表示马力的整数
虽然现在还不清楚发动机如何取得马力这个参数但可以肯定发动机一定支持这个行为而且这是所有发动机惟一值得关注的行为特征这个行为特征既可以用接口定义也可以用抽象类定义为了说明用抽象类定义可能出现的问题下面用抽象类建立发动机的行为模型并用Java方法描述行为代码如下
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象类的基础上构造出多种具体实现例如A型发动机B型发动机等再加上系统的其它部分最后得到版的软件并交付使用一段时间过去了现在要设计版的软件在评估版软件需求的过程中发现一小部分发动机是电池驱动的而电池需要一定的充电时间销售部门的人希望能够通过计算机查阅充电时间根据这一要求定义一个新的行为如图所示
行为查询电驱动发动机的充电时间发动机将返回一个表示充电时间的整数
用Java方法来描述这个行为代码如下
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
在销售部门的软件中电驱动发动机也以类的形式实现但这些类从BatteryPoweredMotor而不是Motor派生这些改动加入到版软件之后销售部门很满意随着业务的不断发展不久之后光驱动的发动机出现了销售部门要求光驱动发动机需要一定光能才能运转光能以流明(Lumen)度量这个信息对客户很重要因为下雨或多云的天气里某些光驱动发动机可能无法运转销售部门要求为软件增加对光驱动发动机的支持所以要定义一个新的行为
行为查询光驱动发动机能够正常运转所需要的最小流明数发动机返回一个整数
再定义一个抽象类并把行为转换成Java方法代码如下
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
选择Java接口还是抽象类
作者俞良松 本文选自开放系统世界-赛迪网 年月日
很多人有过这样的疑问为什么有的地方必须使用接口而不是抽象类而在另一些地方又必须使用抽象类而不是接口呢?或者说在考虑Java类的一般化问题时很多人会在接口和抽象类之间犹豫不决甚至随便选择一种
实际上接口和抽象类的选择不是随心所欲的 要理解接口和抽象类的选择原则有两个概念很重要对象的行为和对象的实现如果一个实体可以有多种实现方式则在设计实体行为的描述方式时应当达到这样一个目标在使用实体的时候无需详细了解实体行为的实现方式也就是说要把对象的行为和对象的实现分离开来既然Java的接口和抽象类都可以定义不提供具体实现的方法在分离对象的行为和对象的实现时到底应该使用接口还是使用抽象类呢?
通过抽象类建立行为模型
在接口和抽象类的选择上必须遵守这样一个原则行为模型应该总是通过接口而不是抽象类定义为了说明其原因下面试着通过抽象类建立行为模型看看会出现什么问题
假设要为销售部门设计一个软件这个软件包含一个发动机(Motor)实体显然无法在发动机对象中详细地描述发动机的方方面面只能描述某些对当前软件来说重要的特征至于发动机的哪些特征是重要的则要与用户(销售部门)交流才能确定
销售部门的人要求每一个发动机都有一个称为马力的参数对于他们来说这是惟一值得关心的参数基于这一判断可以把发动机的行为定义为以下行为
行为查询发动机的马力发动机将返回一个表示马力的整数
虽然现在还不清楚发动机如何取得马力这个参数但可以肯定发动机一定支持这个行为而且这是所有发动机惟一值得关注的行为特征这个行为特征既可以用接口定义也可以用抽象类定义为了说明用抽象类定义可能出现的问题下面用抽象类建立发动机的行为模型并用Java方法描述行为代码如下
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象类的基础上构造出多种具体实现例如A型发动机B型发动机等再加上系统的其它部分最后得到版的软件并交付使用一段时间过去了现在要设计版的软件在评估版软件需求的过程中发现一小部分发动机是电池驱动的而电池需要一定的充电时间销售部门的人希望能够通过计算机查阅充电时间根据这一要求定义一个新的行为如图所示
行为查询电驱动发动机的充电时间发动机将返回一个表示充电时间的整数
用Java方法来描述这个行为代码如下
public abstract BatteryPoweredMotor extends Motor{
abstract public int getTimeToRecharge();
}
在销售部门的软件中电驱动发动机也以类的形式实现但这些类从BatteryPoweredMotor而不是Motor派生这些改动加入到版软件之后销售部门很满意随着业务的不断发展不久之后光驱动的发动机出现了销售部门要求光驱动发动机需要一定光能才能运转光能以流明(Lumen)度量这个信息对客户很重要因为下雨或多云的天气里某些光驱动发动机可能无法运转销售部门要求为软件增加对光驱动发动机的支持所以要定义一个新的行为
行为查询光驱动发动机能够正常运转所需要的最小流明数发动机返回一个整数
再定义一个抽象类并把行为转换成Java方法代码如下
public abstract SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
}
如图所示SolarPoweredMotor和BatteryPoweredMotor都从Motor抽象类派生在整个软件中%以上的代码以相同的方式对待所有的发动机偶尔需要检查一下发动机是光驱动还是电驱动使用instanceof实现代码如下
if (instanceof SolarPoweredMotor){}
if (instanceof BatteryPoweredMotor){}
无论是哪种发动机马力这个参数都很重要所以在所有派生的抽象类(SolarPoweredMotor和BatteryPoweredMotor)中getHorsepower()方法都有效
现在销售部门又有了一种新的发动机它是一种既有电驱动又有光驱动的双重驱动发动机光驱动和电驱动的行为本身没有变化但新的发动机同时支持两种行为在考虑如何定义新型的光电驱动发动机时接口和抽象类的差别开始显示出来了新的目标是在增加新型发动机的前提下尽量少改动代码因为与光驱动发动机电驱动发动机有关的代码已经过全面的测试不存在已知的Bug为了增加光电驱动发动机要定义一个新的SolarBatteryPowered抽象类如果让SolarBatteryPowered从Motor抽象类派生SolarBatteryPowered将不支持针对光驱动发动机和电驱动发动机的instanceof操作也就是说如果查询一个光电驱动的发动机是光驱动的还是电驱动的得到的答案是都不是
如果让SolarBatteryPowered从SolarPoweredMotor(或BatteryPoweredMotor)抽象类派生类似的问题也会出现SolarBatteryPowered将不支持针