第十章 继承 (Inheritance) 继承是面向对象系统的重要特性之一,它支持...
-
date post
22-Dec-2015 -
Category
Documents
-
view
439 -
download
7
Transcript of 第十章 继承 (Inheritance) 继承是面向对象系统的重要特性之一,它支持...
第十章 继承 (Inheritance)
• 继承是面向对象系统的重要特性之一,它支持泛化和特化两方面的抽象。
• 对继承概念的需求: — 一个类型仅适合于结构和操作相同的 对象实例集 — 当类型需要在结构和操作上有所扩充 或变异时如何处理? 方式 1: 定义一个新的类型 问题:⑴缺乏可重用性, ⑵ 缺乏灵活性, 方式 2: 用继承概念定义子类型
示例: persistent type Person is public name,age,spouse,marry body [ name : string; age:int; spouse : Person;] operations declare marry: Person —> void; … implementation define marry(victim) is self.spouse:= victim; … end type Person;
persistent type Employee is public name,age,spouse,marry,ss#,boss body[ …] operations
declare marry : Person —> void;…
implementation define marry (victim) is self.spouse:= victim;
… end type Employee;
引用语句: var mickeyMouse: Employee; miniMouse: Person; mickeyMouse.marry(miniMouse); // 合法 ,miniMouse 是 Person miniMouse.marry(mickeyMouse); // 非法 ,mickeyMouse 非 Person
★ 以上方式无法体现一个雇员也是一个Person 的语义
继承和子类型的一般概念• 继承支持类型间的 is-a 联系• 继承含有三方面语义: 1. — 特化 / 泛化 的抽象语义 2. — 子类型是父类型的一个扩展 3. — 可替换性语义 (Substitutability)
继承的泛化抽象概念• 类型的继承设计是一个抽象过程: 由子类 — 超类是逐级的泛化抽象过程
其中: OTsuper 为 OTsub 的超类, OTsub 为 OTsuper 的子 类, OTsub is-a OTsuper 强调了任何属于 OTsub
的对象也属于 OTsuper 。
OTsuper
OTsub
is-a
OTsuper
OTsub
is-a
OTsuper
OTsub
• 当 OTsub is-a OTsuper 时说明: — ⑴ 子类型 OTsub 的一个实例继承了 OTsuper Type 的所有特征 ( 结构、操 作 ) ,包括 OTsuper 从祖先继承来的 所有特征 — ⑵ 子类型 OTsub 的一个实例还具有 OTsub Type 自定义的独特的结构 和操作• 从类型上看,子类型是超类型的一种扩展
persistent type Employee supertype Person is public ss#,salary,boss,isRetired //public 语句也被继承 body [ss# : int; salary: float; boss : Employee;] operations declare isRetired: —> bool; return (self.age > 64 ); … end type Employee;继承解决了类型间的代码和结构的可复用性
继承的可替换性概念• OTsub is-a OTsuper 从可替换性角度而言,
强调了属于 OTsub 对象实例中的任一个实例对象,也属于 OTsuper 的实例集合
即:若 o ext(OT∈ sub) 则 o ext(OT∈ super)
因此 ext(OTsub) 是 ext(OTsuper) 的一个真子集• 任何一个子类的实例都可以被替换为超
类的实例
Person 类型的特征定义 ●name
●age
●spouse
●marry
Employee 类型的特征定义 ●ss#
●salary
●boss
●isRetired
oPerson
oEmployee
• 子类型 Employee 包含了超类型 Person 的所有属性
• 如果仅针对 Person 特征的访问,客户无法区别被访问的对象是 Person 还是Employee
因此,可以使用替换法则,在所有对Person 引用的地方,用一个 Employee 实例不会破坏一致性
• 解决了类型特化后的灵活性问题• 编译通过放松对类型检查 ( 引用和参数的
哑实结合时)的约束来解决子类型实例对超类型实例的可替换性
type Type1 is type Type2 is type Type3 is
public A →,A← supertype Type1 is supetype Type2 is
body [A:…]; public B→,B ← public C→,C ←
… body [B: …]; body [C: …];
end type Type1; … …
end type Type2; end type Type3;
Type1 A
Type2 B
Type3 C
Types
is-a
is-a
A: …
A: …B: …
A: …B: …C: …
id1
id2
id3
Instances
Type1
Type2
Type3
• 从实例集合的角度来看 超类 Type2 的实例集合是 Type1 集合的
真子集, Type3 是 Type2 的真子集• 类型的实例集合构成了对象的一个类别
— class 的概念• 继承的传递构成了类型的层次结构• 从类型定义的角度来看,子类型是超类
型的一个扩展— 子类型包含了超类型
类型层次结构的根: ANY
• 对于数据库而言,所有对象具有共同特征:
⑴ 对象标识 OID
⑵ 对象标识的测试 — 标识的识别 ⑶ 用户不可见的系统操作• 建立一个根类型为 ANY 来承接所有的共
性操作
persistent type Cylinder is public center1,center2,radius,length,volume body [center1: Vertex; center2: Vertex; radius: float;] operations declare length:→ float; declare volume:→float; … implementation define length is return self.center.distance(self.center2) define volume is return self.radius ** 2.0 * 3.14 * self.length; … end type Cylinder;
persistent type Pipe supertype Cylinder is public innerRadius body [innerRadius:float;] operations declare hollowBodyVolume:→ float; refine volume:→ float; … implementation define hollowBodyVolume is return self.innerRadius ** 2.0 * 3.14 * self.length; define volume is return super.volume-self.hollowBodyVolume; … end type Pipe;
继承概念下得操作重定义• 对于从超类继承而来的操作 , 如果子类型有特
殊的操作扩展 ,GOM 支持对被继承的操作重定义
• 重定义用 refine 标识 , 对操作体进行重写 , 即定义子类型自己的操作版本
• 重定义的作用 : 既支持了操作的继承 , 又支持了子类型的特殊需求
• 子类型仍然可以引用超类中定义的各种算法例如 : 在 pipe 中用 Super.volume 来引用 Cylinder
中的体积计算方法
重定义操作的动态捆绑 (Dynamic Binding)
• 需要有一个控制机制保证对重定义操作的正确引用 , 即保证接受者对象能引用它所需要的最特殊的操作方式
• 该控制机制不能通过静态的类型检查实现 原因 : 可替代性原理支持实例的类型替换 , 而
不考虑是否真需要一个超类型 , 因此 , 在编译时确定操作的版本是不可能的
• 只能在运行时动态检查接受者类型并进行相应操作的动态捆绑 .
示例 : 一个 Cylinder 对象实例库
这是一个合法的 DB 状态— 根据可替换性, id2 , id3 插入到 CylinderSet 中时合法的
{id1,id2,id3}manyCylinders
id0 CylinderSet
center1:id13center2:id23radius:10.0
id1 Cylindercenter1:id37center2:id49
radius:5.0innerRadius:2.5
id2 Pipe
center1:id55center2:id77radius:10.0
innerRadius:5.0radius2:20.0
innerRadius2:10.0
id3 ConicalPipe
示例:在 id0 上的一个体积计算迭代操作 var c:Cylinder;
totalVolume:float;
…
foreach (c in manyCylinders)
totalVolume:=totalVolume + c.volume;
• 在 foreach 的循环中,在语句 c.volume 执行中,控制机制需要随着接受者对象 c 的类型的不同选择相应的 Volume 版本。
一般的动态捆绑选择机制• 从当前接受者对象的直接类型开始,沿着继承的层次结构中指向根 ANY 的路径进行搜索,所遇到的第一个操作的实现体被执行
• 该控制机制能保证相对于接受者对象的最特殊的版本被执行。
• GOM 设计追求的两个基本目标: — 模型的灵活性: 利用继承的子类型定义和可替换性原则提供
了模型的高度灵活性。即一方面可以逐级抽象,另一方面通过重定义操作可以满足子类型的特殊要求,并利用可替换性,用户可以沿类型层次上下结合,变换角色。
— 类型设计的类型安全:通过静态类型检查保证不能发生由于类型的不一致所导致的运行错误
• 这两个要求互相矛盾,如何在保证类型安全下获得最大灵活性
基于可替换性的类型定义 / 检查规则
规则 1 :数据库设计者所施加的静态类型一致性约束必须被执行
— 静态类型约束为: 对于类型 T 的一个对象引用,必须确保该引
用会获得一个类型 T 或 T 的子类的实例对象• 无论何时,无论沿着什么程序控制路径,无论对
象库处于何种状态,该约束必须被遵守。 规则 2 :类型检查要验证一个类型的全部特征 ( 操
作或属性 ) 都是可获取的。即一个操作或属性的引用必须是接受者对象可获取的。
保证类型安全的检查 (1)
• 类型检查按上述的两个规则进行• 类型检查的对象为:变量、属性、集合
和表的元素、操作说明等• 按规则 1 的要求,对表达式赋值号两端
进行类型一致性检查要求::=
超类型 同类型或子类型
• 示例: var somePerson: Person;
someEmployee: Employee;
…
(1) somePerson := someEmployee;
(2) …
(3) someEmployee := somePerson; // 不合法
Employee Person
保证类型安全的检查 (2)• 按规则 2 :被引用 ( 访问 ) 的属性或操作必须
是接受者类型所具有的。• 示例: (1) somePerson.name; // 合法 (2) somePerson.salary; // 不合法 (3) someEmployee.salary; // 合法• 其中,可能 somePerson 会引用一个 Employee
实例,因此语句 (2) 在执行时可能含有正确的引用。但编译器不知道运行时的情况,且不能保证在任何时刻都含有正确的引用,因此是不合法。
Person 类型和 Employee 类型的一个样库示例
name:“Mini Mouse”Age:50
Spouse:id80
miniMouse
id99name:“Mickey Mouse”
Age:60Spouse:id99
ss#:007Boss:id45
Salary:90000.00
Person
mikeyMouse
id80 Employee
name:“Donald Duck”Age:40
Spouse:id1313ss#:4711
Boss:id1717Salary:80000.00
id45 Employee
chief
• 进一步说明的例子: var miniMouse : Person; mickeyMouse : Employee; chief: Employee; i: integer; …(1) mickeyMouse.spouse := miniMouse //ok(2) miniMouse.spouse:= mickeyMouse;//ok(3) mickeyMouse.boss:= chief; //ok(4) miniMouse.spouse.boss:=chief; // 非法(5) i:=mickeyMouse.boss.ss#; //ok(6) i:=miniMouse.spouse.boss.ss#; // 非法(7) i:=miniMouse.spouse.spouse.age; //ok(8) i:=mickeyMouse.spouse.boss.ss#; // 非法(9) mickeyMouse.boss.spouse.marry(chief);
一个类型一致性检查过程示例 miniMouse.spouse.boss := chief;
• 由于可替换性,赋值号左边类型必须是右边类型的一个超类或同类型
Person
Person
潜在的错误
Employee
锚类型约束 (Anchor Type Constraints)
• 锚的概念来自超文本,超文本利用超链联结二个页面,而锚则指明了进入页面的具体位置。 GOM利用了锚的“定位”概念
• 静态类型检查会降低灵活性,而有些灵活性的实现并不会破坏状态的一致性。
类型检查降低灵活性的示例 declare incAge: Person || →Person;
declare incSalary: Employee || float →Employee;
var somePerson : Person;
someEmp:Employee;
(1) somePerson.incAge.incAge;
(2) someEmp.incAge.incAge;
(3) someEmp.incSalary(1000.00).incAge;(4) someEmp.incAge.incSalary(1000.00);// 类型检查报错
• 语句 (3) (4)完成同样功能,而 (4) 无论从什么角度来看,都不会造成 DB 的不一致性。
造成类型检查过严的原因分析及锚的引入
• 类型检查是严格的按操作说明的各个参数类型来检查的
• 有些操作,如 incAge ,其结果类型与接受者类型是相一致的,
如一个雇员增加一岁后仍是雇员• 可以将结果类型与接受者类型捆绑,只要保
证它们二者的类型一致就可以了,不必要一定严格按照类型说明来检查— 锚就起这个作用
GOM 的锚的说明及语义示例: declare incAge: Person || → like self; declare incSalary: Employee || float→ like self;• Like — 象…一样 like self — 指定结果类型与接受者类型 self 一致• Like 还可以指定类型是参数表中的某一个参数类
型,此时,需要对照参数命令• 示例: declare meeting : me:Person,he:Person →like he;• 编译可以根据 like 后表示的各类型来检查结果类
型的合法性
合法操作的精化规则(Legal Operation Refinement)
• 在继承概念下一个重定义操作设计必须遵守规则为 (1) 原操作名不变 (2) 操作参数个数必须与原版本保持一致• 目的:为了保证版本的兼容,即当引用系统进行模式演进或增加新子功能时,能使新不影响原有程序的运行。
• 不影响现有应用的类型扩展使面向对象系统的主要目标
• 在操作说明字句部分,能精化的只是参数类型 ( 接受参数、变元参数、结果参数 ) 的自由度
精化操作的参数类型自由度约束条件• 接受者参数类型 — 只能使原操作所指定的接
受类型的子类型 目的:为了支持精化的要求— 更特殊化• 变元参数类型 — 使原操作的变元参数类型的超类型
目的:为了使操作更能与原祖先的同名操作兼容,参数类型必须更一般化。即新版的同名操作应当照顾它的祖先版本的要求,参数定义更一般化
• 结果参数类型— 使原操作的结果类型的子类型 目的:是精化设计的要求,精化的结果自然应
当是更特殊化
示例说明:不合法的 marry精化定义 declare marry : Person || Person → void code PersonMarriage;
refine marry : Employee || Employee → void code EmployeeMarriage;
define EmployeeMarriage(victim) is
begin
self.spouse:= victim;
self.salary:= self.salary * 1.1; // 婚后需要更多钱养家 end;
不合法的定义导致应用的错误检查: var anEmp: Employee;
aPerson: Person;
anotherPerson: Person;
…
(1) aPerson.marry(anEmp);//ok,marry 的原始版本可用 (2) anEmp.marry(aPerson);// 非法,精化的 marry 操作需要
参数 Employee
(3) anotherPerson:= anEmp; //ok, 可替换 (4) anotherPerson.marry(aPerson);// 不可用,动态绑定
合法的 marry 重定义: declare marry : Person || Person → void code
PersonMarriage; refine marry : Employee || Person → void code Employee; define EmployeeMarriage(victim) is begin self.spouse := victim; self.salary := self.salary * 1.1; end;
示例:每个教授每年出 1 ~ 2篇论文 — 可以由教授和他所指导的学生 (PhD) 共同完成 — 定义一个写论文操作:
declare writePaper : Professor || PhDStudent → Paper code ProfessorWritesPaper;
进一步:一个高级教授应当能让一个不一定是 PhD 的学生也能写获奖论文;改写:
refine writePaper : FullProfessor || Student →
AwardPaper code FullProfessorWritesPaper;
接受参数精化 变参更一般化
结果参数精化
Type Person type Employee supertype ANY is supertype Person is body body [name:string; [ss#:int; age:int; salary:float; spouse:person; refine boss:Employee];!!ILLEGAL boss:Person;]; . . . . . end type Person end type Employee
Var miniMouse:Person;
mickeyMouse:employee;
… !!create instances
(1) miniMouse.boss:=mickeyMouse;
(2) mickeyMouse.boss:=miniMouse;!!illegal
(3) miniMouse.boss.boss:=miniMouse;!!should be legal
强类型约束 (2)
• 在集合 ( 或表 ) 类型之间直接定义继承结构 ,势必损害类型安全 , 它破坏了超类实例集合包含子类集合的原则
type PersonSet is type EmployeeSet is
body body
{Person} {Employee}
… …
end type PersonSet end type EmployeeSet
示例• 如果我们企图将 EmployeeSet 作为
Personset 的子类型 , 从而继承它们所有操作 , 即
Var manyPs:Personset;
manyEmps:EmployeeSet;
(1) manyPs:=manyEmps;
(2) manyPs.insert(somePerson);
• 错误原因 : 语句 (1)产生了对EmployeeSet 的
一个共享 , 即对 Set 类型进行了重定义 , 这显然破坏了超类集合一定包含子类集合的原则 .
因此 , 对于集合类型是不允许重定义的 , 它们只能直接作为根的子类