面向对象的思维方式(转载,作者:未名)

1. 有关术语
Object-Oriented Programming
OOP
面向对象程序设计

2. 为什么OOP能够流行这么久?
被实践证明是成功的
适用于从小型到大型规模的问题
思维方式与解决其他领域的问题的方式类似

3. 范例 
OOP常被描述为一种新的范例。
范例(paradigm)指一种示范性的模型或例子,它提供了一种组织信息的形式

4. 传统的编程和计算模式
用来描述计算机执行程序的传统模型称为过程状态模型,或者称为鸽子笼模型。它主要考虑的是:
状态、变量、赋值、循环等
计算机是一个数据管理器,它遵循一套设计指令,在内存中来回移动,从多个插槽(内存地址)中取出数据,以某种方式改变它们的状态,再把结果放入别的插槽中通过检查插槽中的值,可以确定计算机的状态或计算的结果。
尽管这个模型或多或少地描述了计算机内部所发生的事情,但它却无法帮助我们理解计算机是如何解决问题的,它也不是大多数人(除了鸽子的主人以及邮递工人)用于解决问题的方法。

5. 递归:程序由小的相似的计算部件构成
20世纪70年代,Alan Kay考虑了计算机的传统设计方式,并提出了程序可以由多个小的相似的计算部件构成。如果把每一个部件看成一个代理,由程序可以由小的计算代理所构成。

6. OOP的定义
Alan Kay对OOP的定义:
OOP是基于递归设计的原则的:
--1. 一切都是对象。
--2. 计算通过对象间相互通信,请求其他对象执行动作来实现。对象间通过发送和接收消息来通信。
--3. 每个对象都有自己的内存,其中可能包括了其他的对象。
--4. 每一个对象都是某个类的实例。类就是一组相似的对象。
--5. 类是对象相关行为的储存库。也就是说,同一个类的所有对象都能执行同样的动作。
--6. 类被组织成有单个根节点的树状结构,被称为继承层次结构。与类实例相关的内存和行为都会被树结构中的后代自动继承。

7  OOP概念实例:给朋友送花
例:我要送花给住在另一城市的Sally.
我无法直接送这些花,因此,我将使用了本地花商的服务系统。
我将Sally的地址、花的价钱和品种告诉花商Flora,Flora联系Sally所在城市的花商,后者准备这些花,并与送花人联系送花。
再深入地研究,可以发现还有更多的人参与其中,如花农、种植园主等。
7.1 OOP概念:代理与团队
一个面向对象的程序是由一个相互作用的代理团体组成,这些代理被称作对象。
每一个对象承担一个角色。
每一个对象都提供一种服务或者执行一种动作,以便为团体中其他对象服务。 
7.2  OOP元素: 对象
OOP第1条原则:
:lol: 1. 一切都是对象。
 OOP中的动作是由代理来完成的,这些代理我们称为“实例”( instances)或“对象”(objects). 
在我们设定的场景中,有很多代理一起工作
这些代理包括:我,Sally,花商Flora,Sally所在城市的花商,送花人,花卉批发商,花场主,花农。
每一个代理要扮演一个角色,结果就是当所有工作都做完后,我们的目的就达到了。
7.3  OOP元素: 消息
:lol: OOP第2条原则:
2. 计算通过对象间相互通信,请求其他对象执行动作来实现。对象间通过发送和接收消息来通信。
OOP中的动作是通过对动作请求(称为消息,messenges)的响应来完成的。一个实例可以接收一条消息,然后完成一个动作并返回一个值。
要完成送花的过程,我向Flora发出一条消息,Flora接着向Sally所在城市的花商发出一条消息,后者又向送花人发出一条消息,……
7.4 信息隐藏原则
注意,作为某对象提供的服务的一个用户,我只需要知道对象将接受的消息的名字。
我不需要知道要完成我的要求,需要执行哪些动作。
在接收到一条消息后,对象会负责将该项任务完成。
7.5  OOP元素: 接收者
消息是发给接收这条消息的接收者(receiver)的。
不同的接收者对消息的解释可以不同。
7.6  送达对象不同,对消息的解释也可能不同
var
Flora: Florist;
Elizabeth : Friend;
Kenneth : Dentist;
begin
Flora.sendFlowersTo(Sally);  {可以完成}
Elizabeth.sendFlowersTo(Sally); {也可以完成}
Kenneth.sendFlowersTo(Sally); {也许无法完成}
end;
同一条消息,取决于送达对象的不同,可能得到不同的结果。
7.7 行为、后期绑定
不同的对象即使接收的是同样的消息,对象所完成的动作(行为,behavior)也很可能不同。
确定完成何种行为的决定可以在运行时刻再做,这称为后期绑定(late binding)。
同一个名字表示完成不同的操作,这是多态性(polymorphism)的一种形式。
7.8  OOP元素: 递归设计
:lol: OOP第3条原则:
3. 每个对象都有自己的内存,其中可能包括了其他的对象。
7.9  不干预原则
允许对象以任何它认为合适的不干涉其他对象的方式来完成任务,而不要干预它。
7.10  OOP元素: 类
OOP第4、5条原则:
:lol: 4. 每一个对象都是某个类的实例。类是一组相似的对象
:lol: 5. 类是对象相关行为的储存库(repository)。即同一个类的所有对象都能执行同样的动作。

对Flora行为的预期是根据对所有花商的一般行为来确定
Flora是花商(Florist)类(class)的一个实例(instance)

行为是与类相联系,而不是与单个实例相联系的。
属于某一类的实例的所有对象,对相似的消息采取同样的方法来响应。
7.11 类别的层次
除了知道Flora是花商外,我还知道他是商人、人类、哺乳动物、物质对象。
在每一层次上,都可以了解特定的信息,这些信息适用于所有较低层次。
7.12 类的层次
7.13  OOP元素: 继承
:lol: OOP第6条原则:
6. 类被组织成有单个根节点的树状结构,称为继承层次结构。与类实例相关的内存和行为都会被树结构中的后代自动继承。
在类层次结构中与某层相联系的信息(数据、行为)都会自动地提供地该层次结构的较低层次中。
7.14  OOP元素: 覆盖
子类可以改变其从父类中继承来的信息:
所有哺乳动物是胎生后代的
鸭嘴兽是卵生的哺乳动物
继承与覆盖相结合,体现了面向对象的巨大能力
第2课 抽 象

抽象的定义 
抽象,就是指有意地压缩或隐藏过程或产品细节,以便得到更清晰的表现、细节或结构。
抽象,是控制复杂性时最重要的工具。

抽象实例:地图集 
如果打开一本地图集,一般看到的常是一幅世界地图。该地图只显示了一些最主要的特征,如主要的山脉、海洋等等,但细节基本上都被忽略了。
随后的一系列地图将覆盖小一些的地理区域,也能处理更多的细节。例如,一块大陆(如各大洲)的地图将包括国家的边界和主要的国家。更小的区域(如国家)地图,将包括城市、各山峰的名称。一个城市的地图可能会包括进出该城市的主要道路,再小一些的地图甚至还会画出一些建筑物。

每个抽象层次,包括了某些信息,也忽略了某些信息
我们注意到,在每一个层次上,有些信息被包括进来了,而另一些信息被忽略了。这只是因为在较高的层次上,无法表现所有的细节。即使能够表现,也没有人能够处理这么大量的信息。因此,很多细节被忽略了。
人们通常使用一些简单的工具来建立、理解和管理复杂的系统。其中最重要的技术称为“抽象”(abstraction)。 

信息隐藏
信息隐藏是指在建立抽象表示时有意地省略一些细节。
信息隐藏使得抽象可以控制复杂性。

抽象的层次
在典型的OOP程序中,有许多级抽象。其中,最高的级别是使该程序能够面向对象的部分。
在最高级别上,程序被视为一个对象的“团体”,这些对象间相互作用,以完成共同的目标。

团体:两层含义
在面向对象程序开发过程中,关于“团体”有两个层次的含义:
首先是指程序员的团体,他们在现实世界中相互作用,以便开发出应用程序来。
第二个团体是这些程序员创建的对象的团体,它们在虚拟世界中相互作用,以完成它们的共同目标。
关键之处在于信息隐藏,以及每个层次都进行抽象

抽象的最高级,要突出团体和合作
团体中的每个对象都提供一种被组织中的其他成员使用的服务。
在抽象的最高级别,最重要的是要突出团体和合作,以及成员与其他成员交互的方式。
许多语言允许协同工作的对象组合到一个“单元”(unit)中。例如,Java的“包” packages,C++的“名字空间”name spaces,Delphi中的“单元”(units)。这些单元向单元外的世界显露一些特征,而其他特征则隐藏在单元中。

两层抽象:服务者和客户
两个对象间交互时涉及两层抽象:一个对象(服务者, server)向另一个对象(客户, client)提供服务,二者之间以通信来交互。

术语的用法:server和client
这里的server不是指服务器(如网站服务器,web server),而是指提供服务的服务者。
该个抽象涉及一个关系的两个视角:从客户这一边的视角,及从服务者这一方的视角。

在具有良好的面向对象风格的设计中,我们在描述和讨论服务者提供的服务时,不必考虑客户在使用这些服务时的情况
从服务者这一层次的抽象,要考虑如何实现抽象的行为
最终层次的抽象,就是一个方法,它实现了完成任务所需的操作序列。

确定合适层次的抽象
在软件开发的早期,关键的问题就是确定合适层次的抽象。
既不要忽略太多的细节,也不要包括太多的细节。

抽象形式:分治法
常用的一种抽象形式是将一层划分为多个组成部分。
例如,汽车是由发动机、传动机构、车身和车轮组成的。
要理解汽车这个概念,只要依次检查其组成部件就行了。这就是传统的分治法(divide and conquer).

抽象形式:特殊化(具体化、专门化)
另一种抽象形式称为特殊化或具体化(specialization). 
如,汽车是一个有轮的载运工具,而有轮的载运工具又是一个载运工具。
我们所了解的关于有轮的载运工具的知识,同样适用于汽车和自行车。
我们所了解的关于载运工具的知识,同样适用于马匹和自行车。
面向对象的语言,常常使用这种形式的抽象

抽象形式:不同视角
另一种形式的抽象,是对同一件物品提供不同的视角。每一个视角会强调某一些细节而忽略其他细节,因此,对同一对象描述出不同的特性。
例如,大众眼里的汽车和技工眼里的汽车,看法是很不一样的。

“是一个”与“有一个”抽象
分治法与特殊化,代表了面向对象语言中最常用的两种抽象形式,一般称为“是一个”(is-a)和“有一个”(has-a)抽象。
分治法:“有一个”抽象
分治法是“有一个”抽象。
例如,一辆汽车“有一个”发动机,“有一个”传动机构,等等。
特殊化:“是一个”抽象
特殊化为“是一个”抽象。
例如,自行车“是一个”有轮载运工具,有轮载运工具“是一个”载运工具。


封装
我们用封装(encapsulation)这个术语来严格区分内部视角和外部视角。
封装和替换
封装的重要优点,就是使替换(interchangeability)成为可能。
在将系统分为各组成部分时,各部分间的交互,应减至最小。
例如,对传送机构封装发动机的行为,使我们可以在不同类型的发动机间进行替换。


在软件系统中的应用
将上述思想应用于软件系统,就需要讨论软件组件需要执行的任务,并将它与组件如何完成该任务分隔开来。

抽象形式:分类( Catalogs )
当系统中组件数量变大时,常用分类(Catalogs)来进行组织。
日常生活中常用到不同类型的分类。例如电话号码簿、Internet搜索引擎等。
相似地,软件中也有很多分类。例如,类的列表、类中定义的方法的列表等。

接口与实现
在软件中,我们用“接口”(interface)这个术语来说明一个任务“是什么”(what),用“实现”(implementation)这个术语来说明这个任务“如何做”(how),即分别说明了其外部与内部视角。
接口
接口说明了所设计的系统是干什么的。这是抽象的用户所需要理解的。
接口没有说明任务是如何完成的,因此,它可以匹配所有满足该抽象的实现。
在开发复杂的计算机系统时,一个关键的步骤是将任务划分为各组件。
这样,各部分可以由不同的团队来开发。
每一个组件都有两面,向外界展示的接口,和需要满足该接口需求的实现。
对接口和实现的划分,不仅从高层的角度对设计易于理解,而且使软件组件的替换成为可能。
团体的每一个成员,都提供一种被团队的其他成员使用的服务。
没有一个成员,可以独立解决问题。
只有共同、协调工作,才能完成整体任务。

抽象形式:组合(Composition)
组合,是另一个由简单部分构建复杂结构的有力技术。
其中心思想,是由少量简单的形式,根据一些组合规则,构建出新的形式。
组合中的关键之处,在于既可对初始形式进行组合,也可以对新形式进行组合。

抽象形式:特殊化的层次
另一种处理复杂性的方式,是使用特殊化的层次来构建抽象。
有时这也称为分类法(taxonomy). 
例如,生物分为动物和植物,动物又分为脊椎动物和无脊椎动物,脊椎动物包括哺乳动物,哺乳动物又分为猫、狗……,等等
非标准行为
鸭嘴兽提醒我们,总会有例外(非标准行为)
面向对象的语言,也需要有一种机制来覆盖从上一级继承来的信息。

抽象形式:模式(Patterns)
在我们遇到新问题时,大多数人都会查看已经解决过的老问题中,是否有与新问题相似的情况。以前的问题可以作为一个解决问题的模型,略做修改可能就能解决新问题了。
这就是软件模式(pattern)的思想。

第4章  继    承     

父类属性的扩展
在编程语言里,继承意味着子类的数据和行为是父类相关属性的扩展(一个更大的集)。
子类不仅拥有父类的所有属性,而且还可能另外定义了一些新属性。 

父类形式的简化
另一方面,由于子类是父类的一个更特殊(或称更严格)的形式,因此在某种程度上,也可以说子类是父类的一个简化版。 

扩展与简化
这种把继承作为一种扩展同时也作为一种收缩的思想,正是面向对象技术强大的原因,同时也会在正常的部署中引起混淆 

继承是向下传递的
继承总是向下传递的,因此一个类可以从它上面的多个超类中继承各种属性 
如果Dog是Mammal的子类,而Mammal又是Animal的子类,则Dog不仅继承了Mammal的属性,同时也继承了Animal的属性

子类可以覆盖从父类继承来的行为

可替换性
可替换性是面向对象编程中一种强大的软件开发技术。
可替换性的意思是:变量声明时指定的类型不必与它所容纳的值类型相一致。
这在传统的编程语言中是不允许的,但在面向对象的编程语言中却常常出现。
可替换性常发生在使用接口的时候。 

判断可替换性是否合法的依据
当使用继承从已有的类构建新类时,判断可替换性是否合法的依据如下:
子类实例必须拥有父类的所有数据字段。
子类实例必须实现父类所定义的所有功能,
最起码要通过继承来实现(如果没有明确覆盖的话)
子类也可以定义新的功能,这与可替换性讨论无关。
子类的实例可以模拟父类的行为,在相似的条件下,使用子类实例来替换父类实例时,应该没有区别。

合法性依据并非总是正确
考虑继承的所有使用情况后,上面这条合法性依据并不总是正确。因此,并不是所有通过继承构建的子类都可以代替父类。

子类型
子类型用于描述能明显满足可替换性原则的类之间的关系。
如果满足以下两个条件,则可把类型B称为类型A的子类型。
1:类型B的实例可以合法地赋给声明为类型A的变量。
2:当A类型的变量使用B类型的实例时,不会引起行为的改变

子类与子类型
子类专指使用继承构建新类的机制。
在Java语言中,可以通过extends关键字来辨认。
子类型的关系更抽象,只能通过源程序来松散地体现这种关系。
在大部分例子中,子类也就是子类型。
但也可以使用某种方式来构建不是子类型的子类
此外,还可以使用接口来构造子类型,这就使相关的类型间根本不存在继承关系。
理解这两个概念间的相似性与区别是很重要的。

继承的形式 

特殊化(specialization)继承 
很多情况下,都是为了特殊化才使用继承和子类化的。
在这种形式下,新类是父类的一种特定类型,它能满足父类的所有规范。
用这种方式创建的总是子类型,并明显符合可替换性原则。
与规范化继承一起,这两种方式构成了继承最理想的方式,也是一个好的设计所应追求的目标。

规范化(specification)继承 
规范化继承用于保证子类和父类具有某个共同的接口,即所有的子类实现了具有相同方法头的方法。
父类中既有已实现的方法,也有只定义了方法头、留待子类去实现的方法。子类只是实现了那些定义在父类却又没有实现的方法。
子类并没有重新定义已有的类型,而是去实现一个未完成的抽象规范。
也就是说,父类定义了某些操作,但并没有去实现它。只有子类才能实现这些操作。
在这种情况下,父类有时也被称为抽象规范类。
在Java中,关键字abstract确保了必须要构建子类。声明为abstract的类必须被子类化,不可能用new运算符创建这种类的实例。除此之外,方法也能被声明为abstract,同样在创建实例之前,必须覆盖类中所有的抽象方法。 
规范化继承可以通过以下方式辨认:父类中只是提供了方法头,并没有实现具体的行为,具体的行为必须在子类中实现。

构造(Construction)继承
一个类可以从其父类中继承几乎所有需要的功能,只是改变一些用作类接口的方法名,或是修改方法中的参数列表。
即使新类和父类之间并不存在抽象概念上的相关性,这种实现也是可行的。

扩展继承 
如果子类只是往父类中添加新行为,并不修改从父类继承来的任何属性,即是扩展继承。 
由于父类的功能仍然可以使用,而且并没有被修改,因此扩展继承并不违反可替换性原则,用这种方式构建的子类还是子类型

限制继承 
如果子类的行为比父类的少或是更严格时,就是限制继承。
和扩展继承一样,当程序员以一个现有的类为基类来构建新类时,常常会出现限制继承,只是这个基类不应该、也不能被修改。
限制继承可描述成这么一种技术:它先接收那些继承来的方法,然后使它们无效。 
由于限制继承违反了可替换性原则,用它创建的子类已不是子类型,因此应该尽可能不用。 

合并继承 (多重继承)
可以通过合并两个或者更多的抽象特性来形成新的抽象。 
一个类可以继承自多个父类的能力被称为多重继承


继承方式的总结 
特殊化:子类是父类的一个特例;也就是说,子类是父类的一个子类型。
规范化:父类中定义的行为需要在子类中实现,而父类本身没有实现这些行为。
构造:子类利用父类提供的行为,但并不是父类的子类型。
扩展:子类添加了一些新功能,但并没有改变继承来的行为。
限制:子类限制了一些来自父类的方法的使用。
合并:子类从多个父类中继承特性。

多人参与的编程活动 
一旦可以使用可重用组件来构造程序,编程就从一种个人行为变成了团体的共同努力。
一个程序员既可以是一种新抽象类型的开发者,也可以是别的程序员所开发的软件系统的用户。
我们也常提到由几个对象组成的组织,其中某个客户对象向某种服务的提供者发出服务请求
第6章  多  态  性
多态性(polymorphism),即对象具有多种形态。
在面向对象的编程语言中,多态性是“是一个”关系、消息传递机制、继承、可替换性等概念的自然结果。
面向对象编程(OOP)方法的一个主要优点就是,能把这些概念组合起来使用,进而产生了许多代码共享和复用技术。

6.1 多态性的种类 
纯多态性(pure polymorphism)是指单个函数可用于多个不同类型的参数。
在纯多态性中,只有一个函数(代码块),却可以有多种解释(不同的意思)。
另一种情况是多个不同的函数(代码块)使用同一个名称,这种情形被称为重载(overload),有时也叫特定多态性(ad hoc polymorphism)。
在这两种极端形式之间的是覆盖(override)和延期方法(deferred methods) 

6.2  多态变量
除了方法覆盖,面向对象语言中的多态性只有通过多态变量(polymorphic variables)和可替换性思想来实现。
多态变量具有多面性;也就是说,它能容纳不同类型的值。
多态变量体现了可替换性原则。换句话说,尽管多态变量也有一个所期望的类型,但它能容纳所期望类型的任何子类型值。
对于动态绑定的语言(如Smalltalk),所有的变量可能都是多态的——任何变量都可以容纳任何类型的值。
对于静态类型的语言(如Java),情况会稍微复杂一些。在Java中,多态性是通过变量的声明类型(静态的)与其实际容纳的值类型(动态的)之间的差异来实现的。

6.3  重载(overload)
如果同一个方法名对应于多个方法体,我们就说此方法名被重载(overload)了。注意,重载是覆盖(override)的一个必要部分,但这两种方式是不相同的,重载能够在没有覆盖的情况下单独出现。
对于重载,只有方法名是多态的——它有多种形式。
我们也可以把重载和覆盖想像成另一种方式——只有一种抽象函数,它能接收各种不同类型的参数,而实际执行的代码要由所接收的参数来决定。编译器通常能在编译时确定所使用的方法,然后产生一组惟一的代码,这些代码都是经过优化的
6.3.1  现实生活中的重载消息 
在第1章中,有一个只有重载没有覆盖的例子。
例:为了给朋友一个惊喜,在他生日时我想给他送一束鲜花
一种办法是,向当地的花商发送一条sendFlowersTo消息;
另一种办法是,向我的妻子发送一条同样的消息。
花商和我的妻子都能理解这条消息,两者都能做出反应并取得相同的结果。在某种程度上,我也可以这样认为:sendFlowersTo方法是一种妻子和花商都能理解的方法,但他们却使用了不同的算法来响应我的请求。
特别注意,本例中没有用继承,妻子和花商的共同超类是人(Human)
当然,sendFlowersTo行为并不是人所共有的,如我的牙科医生,虽然他也是人,但如果我向他发送这条消息,他一定会很困惑。 
6.3.2  重载和强制 
例: 要开发一个类库,该类库要表示常用的数据结构
有许多种数据结构可用来容纳一组元素(例如,集合、包、字典、数组、优先队列等)。这些数据结构中都定义了一个add方法,可以在数据结构中插入新元素。
这种情形——用两种完全独立的函数为不同的数据类型提供语义上相似的行动——经常出现在所有的编程语言中,而不只是出现在面向对象的语言中。
例: 重载加号运算符“+”。编译器所产生的整型加法代码一定跟浮点加法所产生的代码有很大区别,但对程序员来说,这些操作都是一样的,都是加法函数。 
在这个例子中,还需要重点指出的一点是,重载并不是所要做的一切。一种语义上独立的操作——强制(coercion),也常常与算术运算有关。
强制用于把一种数字类型转换成另一种数字类型。如果允许混合类型的算术运算,则两个数的相加可以用许多不同的方式来解释:
有可能提供4种不同的函数,分别是:整数+整数、整数+实数、实数+整数和实数+实数。在这种情况下,只有重载但没有强制。
提供两种不同的函数:整数+整数、实数+实数。对于整数+实数和实数+整数,可以把整数强制转换成实数。在这种情况下,把重载和强制结合到了一起。
只提供一种函数:实数+实数。在运算前,所有的参数都被强制转换成实数。在这种情况下,就只有强制而没有重载。
6.3.3  独立类的重载 
有两种不同的重载方式。
一种是,相同的方法名出现在没有继承关系的多个类中;
另一种是,在同一个类中,有多个方法使用相同的名字。

重载并不意味着相似
被重载的方法间不需要有任何的语义相似性。它们只是共用了相同的名称。
这种重载——独立的、互不相干的方法使用同一名称——并没有被认为是不好的风格,一般也不会产生混乱。
实际上,选择一些短的、有意义的名字如add,draw等,有助于理解和正确使用面向对象组件。当需要往集合中加入一个新元素时,调用add方法比调用addNewElement方法,或者调用set_Modul_Addition_Method方法更方便、简明。
所有的面向对象语言都允许在不相关的类中使用相同的方法名。这时候,消息接收者类需要区分被重载的方法名
然而,这并不意味着这些方法可接收任意类型的参数。Java的静态类型特性仍要求方法指定所有的参数类型。

6.3.4  参数重载 
另一种风格的重载——在相同的上下文环境中,多个过程(函数或方法)使用同一个名称,通过参数的个数和类型来区别这些过程,这种重载的方式叫参数重载(parametric overloading)。
这种重载方式不只是出现在Java语言中,许多功能性语言和一些命令式语言(如Ada)也使用了这种重载方式。
参数重载常出现在构造器方法中

6.4  覆盖
在一个类中(通常是抽象超类)定义了一个通用的方法,这个方法要由子类来继承和使用。至少在一个子类中,定义了一个同名的方法,这个方法隐藏了子类实例对通用方法的调用(如果在子类中修订了通用的方法,则子类的方法代替了对通用方法的调用)。我们就说,第二个方法覆盖(override)了第一个方法。
覆盖通常对类用户都是透明的。而且,与重载不同的是,这两个有覆盖关系的方法在语义上被认为是同一体。

6.5  抽象方法
声明为抽象(abstract)的方法也可以认为是延期(deferred)方法。我们在父类中声明它,却留待子类去具体实现。
接口(interface)也被认为是一种延期方法。
这两种情况都被看作是覆盖的一般形式。
在这两种情况下,子类都修改了父类的行为。
对于抽象方法,它的行为实际上是空的,它只起一个占位符的作用,所有定义行为的代码都由子类来提供。 

6.6  纯多态性
许多作者把多态性( polymorphism, 或称纯多态性)术语用于一个方法能接收各种不同类型参数的情形,而用重载(overload)来表示多个方法使用同一个名称。这种用法并没有强加给面向对象语言。例如,在Lisp或ML语言中,很容易写出一个能操作任意类型元素的函数。这个函数是多态的,因为在定义函数的时候,还不知道参数的确切类型。
创建多态函数是面向对象编程的一个最强有力的技术。它允许在更高的抽象层次上编写代码,然后根据需要裁减代码,以适应具体的情况。通常,程序员会向此方法的接收者发送更进一步的消息,以实现裁减。这些更进一步的消息通常与定义多态方法的类无关,而是发送给定义在低层类中的延期方法。

 o 单态系统:值被认为只有一种类型,即所有的数据项必须具有唯一一种类型
o 多态系统:一个值具有多于一种类型的能力。
o 多态系统支持技术:
o (1)强制:避免单态语言的严密性,提供了一种有限的多态形式。必须预先规定类型之间的映射(强制)之间关系。如int型与float型运算,其结果为float型。
o (2)重载:参数的类型化形式将用于选择合适的函数。加函数可对两个整数或两个实数进行运算,参数的类型化信息将被用于合适的函数。

o (3)参数化多态性:一个函数将一致地在某个范围类型中起作用。采用类模板(或称之为类属)来实现。
o (4)包含多态性:在一个父类上定义的函数可以操作任何子类型。采用继承关系来实现。
o 前二者称为特定多态,无原则的形式且仅支持特定数目的类型;后二者称为通用多态性,有原则的方式(通常具有相似的结构和语义),工作于一个无限的类型集合上。 
多态语言的优点:灵活性,抽象,行为共享,代码共享。
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值