`
timnity
  • 浏览: 103844 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

抽象和接口的区别(收藏)

    博客分类:
  • OO
阅读更多
很多人有过这样的疑问:为什么有的地方必须使用接口而不是抽象类,而在另一些地方,又必须使用抽象类而不是接口呢?或者说,在考虑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实现,代码如下:

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所示。

  在使用接口的同时仍旧可以使用抽象类,不过这时抽象类的作用是实现行为,而不是定义行为。只要实现行为的类遵从接口定义,即使它改变了父抽象类,也不用改变其它代码与之交互的方式。特别是对于公用的实现代码,抽象类有它的优点。抽象类能够保证实现的层次关系,避免代码重复。然而,即使在使用抽象类的场合,也不要忽视通过接口定义行为模型的原则。从实践的角度来看,如果依赖于抽象类来定义行为,往往导致过于复杂的继承关系,而通过接口定义行为能够更有效地分离行为与实现,为代码的维护和修改带来方便。
分享到:
评论

相关推荐

    利用QNX screen接口共享屏幕显示图像

    1.QNX screen系统是一个图形框架,提供开发时图像显示功能,抽象对外统一接口,屏蔽底层硬件差异。本资源利用screen图形接口,读取显示设备的显示内容,并将其以窗口形式同步显示,并用thread实时读取原窗口变化。...

    JAVA基础知识精华总结 收藏

    JAVA基础知识精华总结 收藏 1、 对象的初始化 (1) 非静态对象的初始化 在创建对象时,对象所在类的所有数据成员会首先进行初始化。 基本类型:int型,初始化为0。 如果为对象:这些对象会按顺序初始化。 ※在...

    REST API.md

    通过URL来设计系统结构,抽象的是资源,而不是对象、过程,完成的是用户接口 REST API的开发框架介绍:JSR-311,REST Web Services框架 JAX-RS,java接口;### REST Web Application多层框架 REST 应用场景,适合和...

    FastDFS.Net客户端项目 从网上SVN中转过来收藏,非原创.rar

     文件系统是操作系统的一个重要组成部分,通过对操作系统所管理的存储空间的抽象,向用户提供统一的、对象化的访问接口,屏蔽对物理设备的直接操作和资源管理。也就是说,文件系统解决了普通用户使用磁盘存储数据的...

    C#语言规范(4.0版本)

    Microsoft(微软)C#语言规范4.0版本,非常值得大家学习收藏! 目录 1. 简介 1 1.1 Hello world 1 1.2 程序结构 2 1.3 类型和变量 3 1.4 表达式 6 1.5 语句 8 1.6 类和对象 12 1.6.1 成员 12 1.6.2 可访问性 13 1.6.3 ...

    Fundamentals_of_JAVA_Programming:Java编程基础知识-在本课程中进行课堂和家庭练习

    抽象类。 接口。 图形用户界面。 Java Swing- javax.swing软件包。 内部班级。 匿名内部类。 本地课程。 收藏。 泛型。 Lambda表达式。 串流。 例外情况。读写数据到文件: 使用java.io包使用java.nio.file包序列化...

    Spring.net框架

    我不想过多的去谈抽象类和接口的区别,在后续的例子中我们将使用接口。由于接口在进行“动态代理” 时仍能保持类型信息,而抽象类可能由于代理的原因导致继承关系的“截断”(如MixIn等)。除此之外,对于单继承的C#...

    Java基础核心总结.pdf

    Java核心基础知识总结,含思维导图,包含Java基本语法,面向对象,接口和抽象类,异常,内部类,集合,泛型,反射,枚举,I/O,注解等Java基础核心知识,总结全面,内容丰富,欢迎下载。 如果对你有用,麻烦点个收藏...

    java 设计模式资料

    附件中是java实现全部的设计模式,包含代码和工程(jbuilder工程),值得收藏. 此目录里包括了一书中所有23种设计模式的实现(Java 版)源码 关于代码的几点说明: 1. 代码为根据个人对Design Pattern的学习理解写...

    Android代码-微影一款纯粹的在线视频App

    3.接口数据调整 2017.6.7 1.调整gradle配置 2016.10.24 1.调整mvp结构 2.完善设置页面(推荐好友、关于我们、建议反馈) 3.增加分享功能(调用系统分享) 4.增加美女福利页面(瀑布流) 2016.10.11 1.增加意见...

    不收藏不行的史上最全word用法

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    java与模式源码-DesignPatterns:java设计模式与

    在这个学习资料料⼏乎爆炸的时代,甚⾄至你可以轻易就获取⼏个T的视频,⼩手轻⼀点就收藏⼀堆文章,但却很少去看。学习的过程从不只是简单的看一遍就可以,对于一些实操性的技术书籍,如果真的 希望学习到知识,那么...

    digger:用Java编写的“ Digger”游戏的概念验证

    挖掘者 用Java编写的“ Digger”游戏的概念验证 此存储库包含用于名为“ Digger”的简单游戏的概念验证的代码。 概述 在此游戏中,玩家控制一个...游戏的实现包含有效/高效的类层次结构(包括抽象类和接口),通过操作

    程序员考试刷题-I-CS-U1-1-E17:南丹麦大学I-CS-U1-1-E17课程的存储库

    程序员考试刷题I-CS-U1-1-E17 南丹麦大学 I-CS-U1-1-E17 课程的存储库 该存储库将包含我在整个课程中使用的...开始上课抽象类、接口和模式LINQ 7. 2017 年 10 月 24 日 异常处理 8. 2017 年 11 月 7 日 输入输出 9. 20

    promgrpc:用于gRPC驱动的Go应用程序的Prometheus工具库

    相反,它专注于提供由gRPC和Prometheus库导出的接口的实现。 它不会引起副作用,也不会导致全局状态。 取而代之的是,它附带了方便的一线式工具以减少集成开销。 该软件包通过使用控制反转实现了高度模块化。 我们...

    学生公寓管理系统课程设计CSDN下载频道.doc

    因此,可行性研究实质上是要进行一次大大压缩简化了地系统分析和 设计地过程,也就是在较高层次上以抽象地方式进行地系统分析和设计. 二. 系统分析 2.1 需求分析 我校学生公寓现为手工管理,效率低、易出错、手续繁琐...

    一键封装工具标准版5.0.0707

    ·可复制墙纸(C:\WINDOWS\Web\Wallpaper)到[我的文档]下的[图片收藏] ·自动清理多余的桌面右键菜单,智能清除声卡显卡产生的多余启动项 ·集成自主开发的SRS驱动服务清理助手,彻底清理未使用的SRS驱动服务 =====...

Global site tag (gtag.js) - Google Analytics