《UML面向对象设计基础》—第1章1.8多态性

1.8 多态性
UML面向对象设计基础
“polymorphism(多态性)”一词来自两个希腊词,分别表示“许多”和“形态”。多态是指具有许多形态的特性,正如Red Dwarf(异形人)中的情节,宇宙飞船中的全体船员不断被一个可以迅速从一个形体变为另一个形体的异己分子袭击。

面向对象教科书有两种多态性的定义,没有一种能像Red Dwarf的比喻那样生动。下面描述了用符号(A)和(B)标记的两个定义。这两个定义都有效,而且多态性的这两个特性密切相关,并且在面向对象中十分有用。本节的后部分进一步解释这两个定义。

(A)多态性(polymorphism)是一种方法,这种方法使得在多个类中可以定义同一个操作或属性名,并在每个类中可以有不同的实现。

(B)多态性(polymorphism)是一种特性,这种特性使得一个属性或变量在不同的时期可以表示不同类的对象。

假设我们有一个类Polygon,表示二维图形,如图1.20所示。

我们可能对Polygon定义名为getArea的操作,该操作将返回Polygon对象的面积(注意,area是定义在Polygon中的属性,通过继承也是Polygon的子类的属性)。操作getArea的算法非常复杂,因为要考虑不规则的多边形,如图1.20所示。

现在让我们增加一些类,如Trigangle、Rectangle和Hexagon,都是Polygon的子类。这样做是合理的,因为三角形是多边形;矩形是多边形等。参见图1.21。

注意在图1.21中,类Triangle和Rectangle都有名为getArea的操作。这些操作完成与Polygon中的getArea相同的任务:计算由边长围起来的总面积。

但程序的设计者和编程者实现Rectangle的getArea与实现Polygon的getArea存在很大的区别。为什么?因为计算矩形的面积非常简单(长×宽),Rectangle的操作getArea代码非常简单和高效。然而,计算任意复杂多边形的面积的算法则复杂而低效,我们不用该算法计算矩形的面积。

因此,如果我们写一些代码向twoDShape对象发送下列消息:

twoDShape.getArea;
我们可能不知道哪个计算面积的算法将被执行。因为不知道twoDShape属于哪个类。存在五种可能性:

① twoDShape是Triangle的实例。定义为Triangle的操作getArea将被执行。

② twoDShape是Rectangle的实例。定义为Rectangle的操作getArea将被执行。

③ twoDShape是Hexagon的实例。由于Hexagon没有名为getArea的操作,通过继承定义为Polygon的操作getArea将被执行。

④ twoDShape是一般的任意形状的Polygon的实例。定义为Polygon的操作getArea将被执行。

⑤ twoDShape是类C(如Customer)的实例,不是上述的四个类之一。由于C可能没有定义名为getArea 的操作,发送getArea消息将引起编译或运行时错误。这样是合理的,因为twoDShape不应该表示一个客户。

你可能对此感到奇怪,对象不知道发送消息的目标对象是哪个类。然而,这种情况是十分普遍的。例如,下面的最后一行代码,编译时我们不能告诉对象P运行时指向哪个类。实际所表示的对象在最后时刻由用户选择确定(由if语句检测)。

var p:Polygon;

 var t:Triangle:=Triangle.New;

 var h:Hexagon:=Hexagon.New;

 …

 if user says OK

 then p:=t

 else p:=h

 endif:

 …

 p.getArea;      //p可能表示Triangle或Hexagon对象

 …

注意在上面面向对象的代码段中,不需要判断p.getArea执行的是哪一个getArea。这是一个非常便捷的隐藏实现。使得不用改变代码就可增加一个新的Polygon的子类(如Octagon)。隐含着目标对象“知道如何给出面积”,因此发送者不必担心。

再看一下声明语句 var p:Polygon 。这是对于变量 P的多态性的安全约束。在这里使用的编程语法中,P 只表示类Polygon的对象(或Polygon子类的对象)。如果P被赋值Customer对象或Horse对象的句柄,程序将出现运行错而停止执行。

getArea定义为几个类的操作,它为前面列出的标记为(A)的多态性提供一个很好的例子。变量P可以表示几个不同类的对象(如Triangle和Hexagon),是标记为(B)的多态性好例子。整个例子展示了多态性的两个方面是如何一起工作从而简化程序设计。

面向对象环境经常通过动态绑定(dynamic binding)实现多态性。在这种环境中,当消息发送后,在运行期间尽可能靠后地检查消息的目标对象。

动态绑定(dynamic binding)(或运行时绑定或最后绑定)是一种在运行时(而不是在编译时)确定被执行代码的技术。

在上述例子中,操作getArea被定义为Polygon和Triangle的操作,同样说明了覆盖(overriding)的概念。

覆盖(overriding)是指类C定义的方法在C 的一个子类中被重定义。

操作getArea原来是在Polygon中定义的,在Triangle 中被覆盖。Triangle中的操作具有与原来的操作名字相同,但算法不同。

你可能会偶尔使用覆盖技术在C的子类中取消类C的一个操作。可以简单地重定义它返回一个错误来取消一个操作。如果需要大量使用取消操作,源于超类/子类层次结构可能不可靠。

与多态性相关的一个概念是重载(overloading) ,请不要与覆盖(overriding)混淆。

名字或符号的重载(overloading)是指在同一个类中定义的几个操作(或操作符)都具有同一类的名字或符号,我们称该名字或符号为重载。

多态性和重载都要求在运行时选择指定的操作。正如上面的一小段代码中,目标对象的确切类(即将被执行操作的特定实现)直到运行时才知道。

多态性和重载的一般区别在于多态性允许使用相同的操作名在不同的类中定义不同的操作,而重载允许相同的操作名在相同类中定义几次,通常是在相同的名字空间。

选择哪个多态性操作依赖于消息发送的目标对象类。但对于重载操作而言,问题是如何在运行时将正确的程序绑定到操作名?答案是使用消息参数(参数的类或数目)。下面有两个例子:

1a. product1.markDown

1b. product1.markDown (hugePercentage)

2a. matrixl*i

2b. matrixl*matrix2

在第一个例子中,通过操作markDown来降低产品的价格。如果markDown被激活时不带参数(如1a),则该操作使用标准的折扣比例;如果markDown被激活时带有一个参数(如1b中的hugePercentage参数),则操作使用提供的hugePercentage值。

在第二个例子中,乘操作符被重载。如果第二个操作数是整数(如2a),则操作符为标量乘。如果第二个操作数是另一个矩阵(如2b),则操作符*为矩阵乘。

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

时间: 2024-08-31 02:32:37

《UML面向对象设计基础》—第1章1.8多态性的相关文章

《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节提到的面向对象功名录.是否有遗漏?如果是这