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

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。