23
第第第第 第第第第第第第第第 Multiple Inheritance and Multiple Substitutability 第第第第—第 第第第第第 第第第第第第第第第第第第第第 一一 第第第第—第第第第第第第第第第第第第第

第十三章 多重继承和多重替换 Multiple Inheritance and Multiple Substitutability 多重继承 — 当一个子类从一个以上的超 类型继承属性和方法 多从替代

  • View
    348

  • Download
    0

Embed Size (px)

Citation preview

第十三章 多重继承和多重替换Multiple Inheritance and Multiple

Substitutability• 多重继承—当一个子类从一个以上的超

类型继承属性和方法• 多从替代—不同类型的对象之间的相互

替代

多重继承• 现实世界的复杂性 , 由于

一个实体的多哥角色性 ,在进行泛化抽象时 , 常常需要从不同角度进行。反之 , 一个实体可以从不同继承特征 , 构成多重继承。

• 如图 – OT3 继承了 OT1 和 OT2 的

全部特征(属性 , 操作)– OT3 是 OT1 和 OT2 的一个

类型扩展– OT3 的实例既可以替换

OT1 实例 , 也可以替换OT2 实例

多重继承的有向无环图 DAG• 多重继承构造了一个有向

无环图,其总根为 ANY• 类型的层次结构是连通的,

没有孤立子,从根可以到达每一个类型结点,反子亦然

• 类型的层次结构是无回路的,即多重继承不支持递归

• 若只有单重继承,则 DAG转化为树

• GOM 的多重继承是全继承概念

多重继承带来的问题• ( 一 ) 操作 OP 的歧义 -- 操作冲突• 冲突原因:

– 一个 op 名字分别在不同父类 ( 祖先 )OT1, OT2 中说明 , 操作体不同 , 则造成子类 OT3

在全继承情况下 , op 操作名的二义性。– 若 op 在共同的祖先 , 如 OT0 中被定义 , 但在

不同的父类型如 OT1, OT2 中被分别重定义 ,从而造成在 OT3 中的二义性。

操作二义性举例

• 一个操作 bonus( 奖励 ) 分别在 Student 和 Employee有定义 , 当一个奖励作用在” TA” 上时 , 究竟应当怎么处理呢?

declare bonus : Student || float declare bonus : Employee -> void ||float-> void code bonusForStudents; code bonusForEmps;define define bonusForStudents(gpaInc) is bonusForEmps(salInc) is

self.gpa := self.gpa * gpaInc; self.salary := self.salary * salInc;

; 增加一个百分点的 GPA ; 增加一个百分点的 Salary

var bestEmp : Employee;bestStudent : Student;myTA, yourTA : TeachAsst;boss : Manager;

…(1) bestEmp := myTA;(2) bestEmp.bonus(1.1); !! 假定应当增加

salary(3) bestStudent := yourTA;(4) bestStudent.bonus(1.05); !! 假定应当增加 gpa(5) boss.bonus(1.5); !! 不会产生二义性

多重继承的操作冲突解决方案• 注意:为了兼容已有程序 , 不能通过修改

超类中的操作名来解决问题• 解决冲突的方法

– 定义优选权 , 允许用户指定继承的优先权– 用户通过对 Supertype 子句中超类型队列来

指定优先权type OT supertypes OT1,…, OTn is

• 方法 (1) 实现选择 op 的方法 :– A: 首先确定 op 是否在本类型中被定义– B: 按 DAG 图 , 从优先队列中每一个超类开

始 , 向上扫描每个枝条直到根 , 顺序检查 op名称 , 首先被遇到的 op 被选中

解决冲突之方法二 -- 显式重命名• 对所继承的同名的操作 , 分别在当前子类型中

重新命名• 虽然操作 op 的名字在超类中不能修改 , 但在当

前子类中可以用重命名来代替 , 不会影响兼容性。Type OT

Supertypes OT1(renames op to op1 ) … OTm-1(renames op to opm-1 ) OTm,

is …

解决冲突方法三—重定义操作• 对 op 进行重新精化有两种选择:

– A: 在当前子类型 OT3 中重新定义 op 操作– B: 在 OT3 中指定凡遇到 op 操作实际上将与哪一个超类的 op

捆绑。define type OT3

supertypes OT1,OT2 is … operations refine op … implementation define op … self. OT1$op(…); !! 执行 OT1 中定义 op 方法end type OT3;

多重继承带来的冲突二 -- 类型冲突

• 类型冲突是更严重的且目前无法消解的问题– (1) 属性类型冲突 : 从不同的父类分别继承了

一个同名但类型不同的属性• 如 OT1.A 与 OT2.A 类型不相容• 在 OT3 中对同名不相容的属性错误无法在静态编

译时被检查出 , 只有在运行时才能出现。

– ( 2 )操作类型冲突• 从不同的父类分别继承了一个同名但返回类型不同的操作。• Delcare f : OT1 || … - > T1

• Delcare f : OT2 || … - > T2

• 对于一个具有 OT2 类型的变量 O2 有一个引用链 :O2.f(…).q(…), 即函数 q 将由一个 T2 类型的实例引用。

• 而函数程序运行时 , 当 O2 指向一个 OT3 实例时,函数 f 根据优先队列解决方法将选择 OT1 的方法,其返回一个 T1

类型实例 -- 与 q 所要求的相冲突。

类型冲突示例—关于方法 Skill• Student 类型的 Skill 返回一个浮点值 float• Employee 类型的 Skill 返回的一个字符串

var myTA : TeachAst;someStudent : Student;someEmp : Employee;empSkill : string;studentSkill : float;

…(1)someEmp := myTA; someStudent := myTA;(2)empSkill := someEmp.skill;(3)studentSkill := someStudent.skill

多重继承的优点• 多重继承可以支持实体的不同角色的抽象和特

征• 示例 :商业营销系统中的商品概念

– A:商品对象的使用特征-- 类别 ,规格 , 型号 , 产地 ,品牌…

可以逐层抽象– B:商品对象的价值特征

-- 进价 ,售价 ,利润 , 成本 ,供货商 ,合同…也可以从财务处理角度进行管理

多重替换• 多重替换是针对某些多重继承不能清楚

处理的情况下而采用的另外一种支持多角色实体建模的方法

• 多重继承的局限性:– 继承使类型间互相矛盾 , 全继承使类型的属

性集合杂乱无章– 继承可能导致冲突

示例—瑞士军刀• 瑞士军刀有多个部件 ,小刀 ,剪刀 ,钻,等• 瑞士军刀可以以它的某一部件的身份出现

如瑞士军刀是一把小刀; 瑞士军刀是一把剪子; 瑞士军刀是一把钻子 等等

• 瑞士军刀的每一个部件可以单独使用 , 其作用功能完全独立 , 如一把剪刀 , 一把小刀 …

建模分析• 用多重继承概念建模

– 第一步:超类模型 Blade,Knife,Material,ScissorBlade,Scissor均有一个canCut 操作

– 第二步:瑞士军刀建模:type SwissKnife supertypes Knife, Scissor is;… !!见下面的讨论 end type SwissKnife;

• 若采用多重继承方法,缺点为 (1) 属性杂乱无章 ,(2) 方法 canCut 重复定义 ,(3)瑞士军刀的某个部件不能作为一个单独对象来使用

多重替换建模方法• 以瑞士军刀为例

type SwissKnife Supertype Knife is body [ knife : Knife; scissor : Scissor ]; fashion Knife via self.knife; fashion Scissor via self.scissor; …end type SwissKnife;

• 语句 fashion Scissor via self.scissor 的语义为只要瑞士军刀当作 scissor看待,则所有的方法调用均转移到 SwissKnife 的 Scissor 部件

多重替换的引用示例var sk : SwissKnife;

k : Knife;

s : Scissor;

p : WorkPiece;

p.create;

sk.create;

k := sk; s := sk; sk.canCut(p); k.canCut(p); s.canCut(p);

代表 (representation) 问题• 替换概念也可以灵活的应用在两个类型之间• 代表问题示例

– 当总公司召集各部门开会时,其关键在于,要求各部门派一个代表,而并不指定必须是谁

– 部门在开会这种特定条件下,其行为就像一个雇员,例如,一个部门可以插入到会议代表的雇员集合中

– 部门内部需要定义一个“代表”操作,一旦需要一个代表时,该操作可以产生一个具体的雇员

部门和雇员两个类型的替换• 类型定义

type Department supertype ANY is body [ members : {Employee}; manager : Manager; ]; fashion Employee using self.representative; operations declare representative : -> Employee; … implementation define representative is return self.manager; …end type Department;

部门和雇员两个类型的替换• 引用的替换

var meetingParticipants : EmployeeSet;

developmentDep : Department; bigBoss : Manager; someEmp : Employee;…meetingParticipants.insert(bigBoss); !! no meeting without himmeetingParticipants.insert(developmentDep); !! they will send a

representation

foreach (emp in meetingParticipants) print (emp.name);