《UML面向对象设计基础》—第1章1.7节继承

1.7 继承
UML面向对象设计基础
如果你写了一个类C,后来又发现一个类D 除一些额外的属性和操作外与类C几乎是一样的,你会怎么办呢?一种办法是简单地复制C的所有属性和操作,然后将其放到D中。但这种方法不仅增加了额外的工作,而且复制本身也存在维护的麻烦。更好的方法是让类D向类C“请求使用其操作”,这种方法称为继承(inheritance)。

继承(从C到D)是指类D在类C中隐式地定义其每个属性和操作,就好象

这些属性和操作是在类D本身中定义一样。

C称为D的超类。D称为C的子类。

换言之,通过继承,类D的对象可以充分利用类C对象的属性和操作 。

继承代表着面向对象与传统系统方法区别的另一个主要方面。使你可以更加有效地构造软件:

首先,构造类以便处理一般情况。
然后,为处理特殊情况,增加更特殊的类继承首批建立的类。这些新类就可以使用原来类的所有操作和属性(包括类和实例的操作和属性)。
下面的例子有助于说明上述原理。假设在航空应用中有一个类Aircraft。Aircraft可以定义名为turn的实例操作,实例属性名为course。

类Aircraft处理与任何种类的飞行器有关的活动或信息。然而一些特殊的飞行器执行特殊的活动,因此需要特殊的信息。例如,滑翔机执行特殊的活动(如释放拖链)可能需要记录特殊的信息(如是否连接拖链)。

因此,我们可以定义另一个类Glider,使其继承Aircraft。Glider有一个名为release Towline的实例操作和一个名为WeatherTowlineAttached的实例属性(属于类Boolean)。图1.7给出了结构图,空心箭头表示继承。

现在让我们来设想一段面向对象程序的继承机制,首先从类Aircraft和Glider生成对象,然后给这些对象发送消息。在这段程序后面讨论了标记为从(1)到(4)的四条语句。

var ac:Aircraft:= Aircraft.New ;

var gl:Glider:=Glider.New;

…

ac.turn(newCourse,out turnOK);   (1)

gl.releaseTowline;           (2)

gl.turn(newCourse,out turnOK);   (3)

ac.releaseTowline;(4)

① ac表示由ac指定的对象接收消息 turn(newCourse,out turnOK) ,执行操作turn (带有适当参数)。因为ac是Aircraft的一个实例,ac将直接使用在类Aircraft中已经定义的操作turn。
② gl表示由gl指定的对象接收消息releaseTowline,执行操作releaseTowline(不需要参数)。
  因为gl是Glider的一个实例,gl将直接使用在类Glider中已经定义的操作releaseTowline。

③ gl表示由gl指定的对象接收消息turn(newCourse,out turnOK),执行操作turn(带有适当参数)。如果没有定义继承,这个消息在执行时会出错(如无定义操作-turn),因为gl是Glider的实例,而Glider中没有名为turn的操作。
  然而,由于Aircraft是Glider的超类,对象gl也被允许使用Aircraft的任何操作。如果Aircraft有一个超类FlyingThing,gl将被允许使用FlyingThing的任何操作。因此,标记为(3)的代码行可以正确运行,在Aircraft中定义的操作turn也会被执行。

④ 这行代码不会执行!ac为Aircraft的实例,没有定义名为releaseTowline 的操作。继承在这里用不上,只有Glider类定义了releaseTowline操作,并且Glider是Aircraft的子类。由于从Glider 到Aircraft方向没有继承,系统将由于错误而停止运行。这样设计是合理的,因为ac可能表示大型喷气式飞机,操作releaseTowline对其没有意义。

在1.6节讨论了类和对象的区别。现在我们知道对象和实例之间也存在细微的差别。尽管目前为止我们将“对象”和“实例”几乎同样对待,在某种意义上继承允许一个对象同时是多个类的实例。

在现实世界中也是如此。如果你拥有一个滑翔机,当然完全拥有一个尾部具有标识(句柄)的对象。这架滑翔机(显然)是滑翔机的一个例子,同时也是飞行器的例子。概念上讲,你拥有的这个对象是Glider的实例,也是Aircraft的实例。

  实际上,上述例子说明了一个有效使用继承的有效测试。如果可以说“某个D是某个C”,则D差不多就是C的子类。因此,既然可以说“滑翔机是某种飞行器”,则类Glider应该是类Aircraft(飞行器)的子类,在第10、11和12章将进一步讨论继承的正确使用方法。

让我们进一步探讨继承。gl表示由gl引用的对象在运行时是两部分的融合。一部分是为Glider定义的实例操作和实例属性;另一部分是为Aircraft定义的实例操作和实例属性。如图1.18所示。

在大多数语言中,继承子类继承超类所提供的一切信息;子类不能有选择地继承。但可以通过技巧使子类略去继承的操作,这将在第1.8节讨论。

采用好的面向对象语言实现继承的实际代码非常直观。只需声明每个子类继承哪个超类。例如:

class Glider inherits from Aircraft;

…

这个继承的例子为单继承,指每个类至多有一个直接超类。此外,还有多继承的情形。多继承指每个类可以有任意多个直接超类。

多继承能将单继承的继承树转换为继承框架,如图1.19所示。

多继承引入了一些设计上的难题,因为子类从多个父辈继承,可能引起继承操作或属性的冲突。冲突的操作具有相同的名字,继承子类不能容易地表述继承的是哪个操作。
诸如命名冲突问题影响了多继承的声誉。多年来,对多继承的谴责和赞成已经到了炙热化的程度。我声明我是赞同多继承的,因为现实中需要频繁使用多继承的子类。正如图1.19所示,类PassengerAircraft合理地从类Aircraft和类PessengerVehicle获得继承。

因为多继承可能建立复杂和难以理解的结构,因此,使用多继承比使用单继承要更加慎重。目前为止,C++和Eiffel两个主要语言支持多继承,而另外两个主要语言Java和Smalltalk则不支持多继承。

本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。

时间: 2024-09-20 00:38:11

《UML面向对象设计基础》—第1章1.7节继承的相关文章

《UML面向对象设计基础》—第1章1.10节本章小结

1.10 本章小结 UML面向对象设计基础 因为"面向对象"在英语中就没有绝对优势的定义,因此,针对面向对象定义的特性从来就没有一致的意见.我本人认为以下特性对面向对象是至关重要的:封装.信息/实现隐藏.状态保持.对象标识.消息.类.继承.多态性及一般性. 面向对象的封装产生一个由表示对象状态的属性及其操作组成的软件结构("对象"),用实现的术语讲,就是操作的方法处理保持对象状态的变量.封装确保对对象内部信息的任何修改(或访问)都必须通过对象的操作. 信息/实现隐藏

《UML面向对象设计基础》—第1章1.9节一般性

1.9 一般性 UML面向对象设计基础 一般性(genericity)指一个或多个类内部使用的类C的结构,仅在运行时(即示例类C的对象时)才提供. 说明一般概念的最好方式是讲述一个不堪回首的故事.当时我还是一名大学生,学习一门数据结构(Data Structures)的课程101.有一个学期,Rossini 教授给我们留了一项作业,设计和编程整数有序平衡二叉树(见图1.22).平衡二叉树的主要特点是所有的叶子在同层上拉平. 在往树中插入另一个整数(如5 ,见图1.23)之前,一切顺利.插入整数5

《UML面向对象设计基础》—第2章2.4节面向对象的益处

2.4 面向对象的益处 UML面向对象设计基础 本节的题目既迎合愤世嫉俗者又符合盲从者. 一些反对者可能会说面向对象没有什么优点:它仅是一种流派或是一场从西方一些地区引发起的全球性阴谋.而一些激进派则宣称面向对象是一流的并且是所有软件成功的唯一途径.面向对象不仅适用于Windows系统,而且还适用于无所不能的分布式Web体系结构. 这两种说法都太极端.作者认为面向对象是有用的,但不能神化,它还不够完美,其特定实用程序依赖于在软件开发过程中的使用方法. 没有一种有价值的软件工程方法可以成为"当年时

《UML面向对象设计基础》—第2章2.1节面向对象的起源

第2章 面向对象简史 UML面向对象设计基础 前面讨论了面向对象的固有特性,现在来看一下面向对象是如何适用于软件开发的广阔领域. 当听说Wolfgang Pauli教授提出一种新的基本粒子(μ介子,μ- meson或muon)时,Isidor I .Rabi教授曾经立即予以反驳:"谁承认这种说法?"鉴于此事例,我在本章开头部分列出一些对面向对象有贡献的人士.下面将面向对象置于社会环境中,讨论对这种软件方法的看法.然后,将面向对象置于工程环境中,将面向对象与电子学做一个类比.最后,阐述面

《UML面向对象设计基础》—第2章2.2节面向对象的成熟期

2.2 面向对象的成熟期 UML面向对象设计基础 本节介绍软件工业如何促进面向对象时代的真正到来. 老生物学家们常爱发表这样的言论:个体生物重演了生物发展史.其含义是个体生物胚胎的发展通常演绎着生物整体发展的进化过程(如人类胚胎的发育过程).当然在时间上存在很大的差异.个体生物的发展可能只有几个月,而整体生物却世代繁衍. 尽管老生物学家的说法无庸置疑,但在软件工程中却出现了一种新观点:面向对象软件工程的历史重演了传统软件工程的历史.当然在时间跨度上也存在很大的差异.形成成熟的过程和数据库结构花费

《UML面向对象设计基础》—第2章2.5节本章小结

2.5 本章小结 UML面向对象设计基础 在面向对象中有一种说法,即面向对象构件与电子集成电路有些类似.至少在软件中有机会象现代电子工程师那样构造系统:可以通过预制构件的连接实现有效的抽象.但为了实现这一点,首先必须标识正确的软件抽象,然后以构造的方式进行联接. 要想掌握"软件集成电路"的技术,就必须广泛而深入地研究面向对象."面向对象"是一个术语,融合了从20世纪60年代到现在许多软件研究者的思想.但并非所有人都赞成这个观点.一些激进派认为面向对象是对过去的一个完

《UML面向对象设计基础》—第1章1.5节消息

1.5 消息 UML面向对象设计基础 对象通过消息请求另一个对象执行活动.许多消息还具有将信息从一个对象传送给另一个对象的作用.大多数老前辈都将消息列为重要的面向对象特性. 消息(message)是发送对象obj1向目标对象obj2发送请求的载体,申请对象obj2的一个方法. 本节对消息特性进行了剖析,描述了消息参数的特点,发送消息对象的角色,接收消息对象的角色及消息的三种类型. 1.5.1 消息结构 消息由几个含义组成,每个含义在面向对象设计中都十分重要.实际上,本书从头到尾会多次用到消息的特

《UML面向对象设计基础》—第1章1.3节状态保持

1.3 状态保持UML面向对象设计基础面向对象的第三个抽象概念是对象具有保持状态的能力.当传统的过程模块(函数.子程序.过程等)返回到调用者时,不会带来任何负作用,模块运行结束,只是将其结果返回.当同一模块再次被调用时就象是第一次诞生一样.模块对以前的存在没有任何记忆,就像人类一样对以前的存在一无所知. 但对于对象而言,如机器人对象就知道它的过去.对象在其自身内部将信息保留一段时间.例如,一个对象的"调用者"可能给该对象一个信息,后来该调用者或其他调用者又要求该对象再次提供这一信息.也

《UML面向对象设计基础》—第2章2.3节类似工程学的面向对象

2.3 类似工程学的面向对象UML面向对象设计基础20世纪80年代,Brad Cox用某种类似现代生活中用到的硬件集成电路(IC)的方式看待软件对象(参见[Cox,1986]).当华盛顿大学的考古学家们在我办公室的一堆论文中寻找时,我想起了这个类比.他们发现一本Merrill Skolnik著的<雷达系统导论>(Introduction to Radar System),在书中Skolnik先生提出下面的观点: 电子工程可以根据以下分类: 组件:技术:系统.组件是可以组合的基本构造单元,通过使

《UML面向对象设计基础》—第2章2.6节习题

2.6 习题UML面向对象设计基础① 本书引用了大量比喻.随着对本书内容的体会,分析软件对象类与电子集成电路之间类比的缺陷. ② 根据你对面向对象的了解,你属于反对派.进化派.激进派中的哪一种?通过面向对象的开发与传统主流软件开发的对比,调整你的位置. ③ 你认为企业有必要选择一种语言(开发环境)具有第一章描述的面向对象的所有特性吗?换言之,你赞同一些面向对象的激进派对一些企业选择部分面向对象特性而不是全部面向对象特性持讥讽态度吗? ④ 回顾一下2.1节提到的面向对象功名录.是否有遗漏?如果是这