java的⾯向接⼝编程思想_JAVA⾯向接⼝的编程思想与具体实
现
⾯向对象设计⾥有⼀点⼤家已基本形成共识,就是⾯向接⼝编程,我想⼤多数⼈对这个是没有什么觉得需要怀疑的。
问题是在实际的项⽬开发中我们是怎么体现的呢? 难道就是每⼀个实现都提供⼀个接⼝就了事了?反过来说,你有时候有没有觉得接⼝是多余的事? ⼜或者,你仅仅是觉得现在类似spring这样的框架已习惯⽤接⼝这种⽅式⽽⼼存当然。
设计模式解析⾥提到了⾯向对象设计考虑的⼏个视⾓,⼀个是概念层,⼀个是规约层,⼀个是实现层。我如果没有猜错的话,实际上我们⼤多数⼈的眼睛⼀直是盯着实现层的,⽽这正是⾯向对象设计所极⼒避免的,即你不要在⼀开始就关注这些细节,你要关注的是规约(接⼝).
对于实际项⽬开发来说,如果我们把实现的过程分为多个阶段的话我们不妨这么划分,第⼀阶段,根据client端的需要去设计我们的规约(interface),在这个阶段任何实现都没有,所有的任务就是定义接⼝所需要的职责,以及所需要的⼀些po,vo;第⼆阶段,实现前⾯定义的规约.
以前我是怎么做的呢?
我是交叉作的,即假模假样的定义⼀个接⼝(其实我⼼⾥在想这个东西有屁⽤),然后定义了⼀个⽅法,然后就⽴即去实现这个⽅法,再然后我⼜定义⼀个⽅法,继续去实现,我现在终于想通了,这样好累,效率很低,最重要的是,这不属于真正的设计。
现在我是怎么做的呢?⽐如⼀个list.jsp⾥需要查询,列表,然后看明细信息,然后增加信息,我会第⼀步在接⼝⾥定义完(这个过程会有整体设计的意识),毫不关⼼底层实现(数据库、事务),我的⽬标就是"我想要这个功能,我想要那个功能",⾄于那个功能怎么实现在第⼀阶段我认为那不是我的事情(尽管这个事情最终还是由我来做)
.⼤家看这个过程和前⾯的过程有什么本质的不同呢?
就是分层的概念更加明显,你的⼯作更有层次,每次都有先设计再实现的步骤,⽽前⾯那个过程很容易就让你不知不觉地陷⼊纯实现的陷阱中。
什么叫⾯向接⼝编程?
在⼀个⾯向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现的对系统设计⼈员来讲就不那么重要了;⽽各个对象之间的协作关系则成为系统设计的关键。⼩到不同类之间的通信,⼤到各模块之间的交互,在系统设计之初都是要着重考虑的,
这也是系统设计的主要⼯作内容。⾯向接⼝编程我想就是指按照这种思想来编程吧!实际上,在⽇常⼯作中,你已经按照接⼝编程了,只不过如果你没有这⽅⾯的意识,那么你只是在被动的实现这⼀思想;表现在频繁的抱怨别⼈改的代码影响了你(接⼝没有设计到),表现在某个模块的改动引起其他模块的⼤规模调整(模块接⼝没有很好的设计)等等。
Booch先⽣那天谈到Interaction Designer,它就是指做这类设计的⼈,只不过层次更⾼⼀些。我想⽬前我们的软件设计队伍中,这类⼈是最缺乏的⼈才之⼀。
⾮接⼝编程?是不是就是⾯向过程的编程思想?
1.关于接⼝的理解
接⼝从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
我们在⼀般实现⼀个系统的时候,通常是将定义与实现合为⼀体,不加分离的,我认为最为理解的系统设计规范应是所有的定义与实现分离,尽管这可能对系统中的某些情况有点繁烦。
接⼝的本⾝反映了系统设计⼈员对系统的抽象理解。
接⼝应有两类:
第⼀类是对⼀个体的抽象,它可对应为⼀个抽象体(abstract class);
第⼆类是对⼀个体某⼀⽅⾯的抽象,即形成⼀个抽象⾯(interface);
⼀个体有可能有多个抽象⾯。
抽象体与抽象⾯是有区别的。
2.设计接⼝的另⼀个不可忽视的因素是接⼝所处的环境(context,environment),系统论的观点:环境是系统要素所处的空间与外部影响因素的总和。任何接⼝都是在⼀定的环境中产⽣的。因此环境的定义及环境的变化对接⼝的影响是不容忽视的,脱离原先的环境,所有的接⼝将失去原有的意义。
3.按照组件的开发模型(3C),它们三者相辅相成,各司⼀⾯,浑然⼀体,缺⼀不可。
⾯向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及⽅法
⾯向过程是指,我们考虑问题时,以⼀个具体的流程(事务过程)为单位,考虑它的实现
接⼝设计与⾮接⼝设计是针对复⽤技术⽽⾔的,与⾯向对象(过程)不是⼀个问题
我认为:UML⾥⾯所说的interface是协议的另⼀种说法。并不是指com的interface,CORBA的interfac
e,Java的interface,Delphi的interface,⼈机界⾯的interface或NIC的interface。
在具体实现中,是可以把UML的interface实现为语⾔的interface,分布式对象环境的interface或其它什么interface,但就理解UML的interface⽽⾔,指的是系统每部分的实现和实现之间,通过interface所确定的协议来共同⼯作。
所以我认为,⾯向interface编程,原意是指⾯向抽象协议编程,实现者在实现时要严格按协议来办。也就是BillJoy同志说的,⼀边翻rfc,⼀边写代码的意思。⾯向对象编程是指⾯向抽象和具象。抽象和具象是⽭盾的统⼀体,不可能只有抽象没有具象。⼀般懂得抽象的⼈都明⽩这个道理。
但有的⼈只知具象却不知抽象为何物。
所以只有interface没有实现,或只有实现⽽没有interface者是没有⽤的,反OO的。
所以还是⽼⽼实实⾯向对象编程,⾯向协议编程,或者什么都不⾯向,⽼⽼实实编程。
但是我很讨厌讨论这样的术语,不如我们谈谈什么叫⾯向领导的编程?⾯向⽤户的编程?领导和⽤户有时都很BT,我们就⾯向BT编程?
选择Java接⼝还是抽象类?
很多⼈有过这样的疑问:为什么有的地⽅必须使⽤接⼝⽽不是抽象类,⽽在另⼀些地⽅,⼜必须使⽤抽象类⽽不是接⼝呢?或者说,在考虑Java类的⼀般化问题时,很多⼈会在接⼝和抽象类之间犹豫不决,甚⾄随便选择⼀种。
实际上接⼝和抽象类的选择不是随⼼所欲的。要理解接⼝和抽象类的选择原则,有两个概念很重要:对象的⾏为和对象的实现。如果⼀个实体可以有多种实现⽅式,则在设计实体⾏为的描述⽅式时,应当达到这样⼀个⽬标:在使⽤实体的时候,⽆需详细了解实体⾏为的实现⽅式。也就是说,要把对象的⾏为和对象的实现分离开来。既然Java的接⼝和抽象类都可以定义不提供具体实现的⽅法,在分离对象的⾏为和对象的实现时,到底应该使⽤接⼝还是使⽤抽象类呢?
在接⼝和抽象类的选择上,必须遵守这样⼀个原则:⾏为模型应该总是通过接⼝⽽不是抽象类定义。为了说明其原因,下⾯试着通过抽象类建⽴⾏为模型,看看会出现什么问题。
假设要为销售部门设计⼀个软件,这个软件包含⼀个“发动机”(Motor)实体。显然⽆法在发动机对象中详细地描述发动机的⽅⽅⾯⾯,只能描述某些对当前软件来说重要的特征。⾄于发动机的哪些特征是重要的,则要与⽤户(销售部门)交流才能确定。
销售部门的⼈要求每⼀个发动机都有⼀个称为马⼒的参数。对于他们来说,这是惟⼀值得关⼼的参数。基于这⼀判断,可以把发动机的⾏为定义为以下⾏为。
⾏为1:查询发动机的马⼒,发动机将返回⼀个表⽰马⼒的整数。
虽然现在还不清楚发动机如何取得马⼒这个参数,但可以肯定发动机⼀定⽀持这个⾏为,⽽且这是所有发动机惟⼀值得关注的⾏为特征。这个⾏为特征既可以⽤接⼝定义,也可以⽤抽象类定义。为了说明⽤抽象类定义可能出现的问题,下⾯⽤抽象类建⽴发动机的⾏为模型,并⽤Java⽅法描述⾏为1,代码如下:
代码
public abstract Motor{
abstract public int getHorsepower();
}
在Motor抽象类的基础上构造出多种具体实现,例如A型发动机、B型发动机等,再加上系统的其它部分,最后得到1.0版的软件并交付使⽤。⼀段时间过去了,现在要设计2.0版的软件。在评估2.0版软件需求的过程中,发现⼀⼩部分发动机是电池驱动的,⽽电池需要⼀定的充电时间。销售部门的⼈希望能够通过计算机查阅充电时间。根据这⼀要求定义⼀个新的⾏为,如图1所⽰。
⾏为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实现,代码如下:
代码
java类的概念if (instanceof SolarPoweredMotor){...}
if (instanceof BatteryPoweredMotor){...}
⽆论是哪种发动机,马⼒这个参数都很重要,所以在所有派⽣的抽象类(SolarPoweredMotor和BatteryPoweredMotor)
中,getHorsepower()⽅法都有效。
现在销售部门⼜有了⼀种新的发动机,它是⼀种既有电驱动⼜有光驱动的双重驱动发动机。光驱动和电驱动的⾏为本⾝没有变化,但新的发动机同时⽀持两种⾏为。在考虑如何定义新型的光电驱动发动机时,接⼝和抽象类的差别开始显⽰出来了。新的⽬标是在增加新型发动机的前提下尽量少改动代码。因为与光驱动发动机、电驱动发动机有关的代码已经过全⾯的测试,不存在已知的Bug。为了增加光电驱动发动机,要定义⼀个新的SolarBatteryPowered抽象类。如果让SolarBatteryPowered从Motor抽象类派⽣,SolarBatteryPowered将不⽀持针对光驱动发动机和电驱动发动机的instanceof操作。也就是说,如果查询⼀个光电驱动的发动机是光驱动的,还是电驱动的,得到的答案是:都不是。
如果让SolarBatteryPowered从SolarPoweredMotor(或BatteryPoweredMotor)抽象类派⽣,类似的问题也会出
现,SolarBatteryPowered将不⽀持针对BatteryPoweredMotor(或SolarPoweredMotor)的instanceof操作。从⾏为上看,光电驱动的发动机必须同时从两个抽象类派⽣,但Java语⾔不允许多重继承。之所以会出现这个问题,根本的原因在于使⽤抽象类不仅意味着定义特定的⾏为,⽽且意味着定义实现的模式。也就是说,应该定义⼀个发动机如何获得⾏为的模型,⽽不仅仅是声明发动机具有某⼀个⾏为。
如果⽤接⼝来建⽴⾏为模型,就可以避免隐含地规定实现模式。例如,前⾯的⼏个⾏为改⽤接⼝定义如下。
⾏为1:
代码
public interface Motor(){
public int getHorsepower();
}
⾏为2:
代码
public interface BatteryPoweredMotor extends Motor(){
public int getTimeToRecharge();
}
⾏为3:
代码
public interface SolarPoweredMotor extends Motor{
abstract public int getLumensToOperate();
}
现在光电驱动的发动机可以描述为:
代码
public DualPoweredMotor implements SolarPoweredMotor, BatteryPoweredMotor{}
DualPoweredMotor只继承⾏为定义,⽽不是⾏为的实现模式,如图2所⽰。
在使⽤接⼝的同时仍旧可以使⽤抽象类,不过这时抽象类的作⽤是实现⾏为,⽽不是定义⾏为。只要实现⾏为的类遵从接⼝定义,即使它改变了⽗抽象类,也不⽤改变其它代码与之交互的⽅式。特别是对于公⽤的实现代码,抽象类有它的优点。抽象类能够保证实现的层次关系,避免代码重复。然⽽,
即使在使⽤抽象类的场合,也不要忽视通过接⼝定义⾏为模型的原则。从实践的⾓度来看,如果依赖于抽象类来定义⾏为,往往导致过于复杂的继承关系,⽽通过接⼝定义⾏为能够更有效地分离⾏为与实现,为代码的维护和修改带来⽅便。
Java接⼝特性学习
在Java中看到接⼝,第⼀个想到的可能就是C++中的多重继承和Java中的另外⼀个关键字abstract。从另外⼀个⾓度实现多重继承是接⼝的功能之⼀,接⼝的存在可以使Java中的对象可以向上转型为多个基类型,并且和抽象类⼀样可以防⽌他⼈创建该类的对象,因为接⼝不允许创建对象。
interface关键字⽤来声明⼀个接⼝,它可以产⽣⼀个完全抽象的类,并且不提供任何具体实现。interface的特性整理如下:
1. 接⼝中的⽅法可以有参数列表和返回类型,但不能有任何⽅法体。
2. 接⼝中可以包含字段,但是会被隐式的声明为static和final。
3. 接⼝中的字段只是被存储在该接⼝的静态存储区域内,⽽不属于该接⼝。
4. 接⼝中的⽅法可以被声明为public或不声明,但结果都会按照public类型处理。
5. 当实现⼀个接⼝时,需要将被定义的⽅法声明为public类型的,否则为默认访问类型,Java编译器不允许这种情况。
6. 如果没有实现接⼝中所有⽅法,那么创建的仍然是⼀个接⼝。
7. 扩展⼀个接⼝来⽣成新的接⼝应使⽤关键字extends,实现⼀个接⼝使⽤implements。
interface在某些地⽅和abstract有相似的地⽅,但是采⽤哪种⽅式来声明类主要参照以下两点:
1. 如果要创建不带任何⽅法定义和成员变量的基类,那么就应该选择接⼝⽽不是抽象类。
2. 如果知道某个类应该是基类,那么第⼀个选择的应该是让它成为⼀个接⼝,只有在必须要有⽅法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在⼀个或多个被具体实现的⽅法,只要⽅法没有被全部实现该类就仍是抽象类。
以上就是接⼝的基本特性和应⽤的领域,但是接⼝绝不仅仅如此,在Java语法结构中,接⼝可以被嵌套,既可以被某个类嵌套,也可以被接⼝嵌套。这在实际开发中可能应⽤的不多,但也是它的特性之⼀。需要注意的是,在实现某个接⼝时,并不需要实现嵌套在其内部的任何接⼝,⽽且,private接⼝不能在定义它的类之外被实现。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论