第十三章 多重继承和多重替换 Multiple Inheritance and Multiple Substitutability 多重继承 — 当一个子类从一个以上的超 类型继承属性和方法 多从替代 — 不同类型的对象之间的相互 替代
多重继承 现实世界的复杂性, 由于 一个实体的多哥角色性, 在进行泛化抽象时, 常常 需要从不同角度进行。 反之, 一个实体可以从不 同继承特征, 构成多重继 承。 如图 –OT 3 继承了 OT 1 和 OT 2 的全 部特征(属性, 操作) –OT 3 是 OT 1 和 OT 2 的一个类 型扩展 –OT 3 的实例既可以替换 OT 1 实例, 也可以替换 OT 2 实例
多重继承的有向无环图 DAG 多重继承构造了一个有向 无环图,其总根为 ANY 类型的层次结构是连通的, 没有孤立子,从根可以到 达每一个类型结点,反子 亦然 类型的层次结构是无回路 的,即多重继承不支持递 归 若只有单重继承,则 DAG 转化为树 GOM 的多重继承是全继承 概念
多重继承带来的问题 ( 一 ) 操作 OP 的歧义 -- 操作冲突 冲突原因: – 一个 op 名字分别在不同父类 ( 祖先 )OT 1, OT 2 中说明, 操作体不同, 则造成子类 OT 3 在全继 承情况下, op 操作名的二义性。 – 若 op 在共同的祖先, 如 OT 0 中被定义, 但在不 同的父类型如 OT 1, OT 2 中被分别重定义, 从 而造成在 OT 3 中的二义性。
操作二义性举例 一个操作 bonus( 奖励 ) 分别在 Student 和 Employee 有定义, 当一个奖励作用在 ” TA ” 上时, 究竟应当怎 么处理呢?
declare bonus : Student || float declare bonus : Employee -> void ||float-> void code bonusForStudents; code bonusForEmps; 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 OT 1, …, OT n is
方法 (1) 实现选择 op 的方法 : –A: 首先确定 op 是否在本类型中被定义 –B: 按 DAG 图, 从优先队列中每一个超类开始, 向上扫描每个枝条直到根, 顺序检查 op 名称, 首先被遇到的 op 被选中
解决冲突之方法二 -- 显式重命名 对所继承的同名的操作, 分别在当前子类型中重 新命名 虽然操作 op 的名字在超类中不能修改, 但在当前 子类中可以用重命名来代替, 不会影响兼容性。 Type OT Supertypes OT 1 (renames op to op 1 ) … OT m-1 (renames op to op m-1 ) OT m, is …
解决冲突方法三 — 重定义操作 对 op 进行重新精化有两种选择: –A: 在当前子类型 OT 3 中重新定义 op 操作 –B: 在 OT 3 中指定凡遇到 op 操作实际上将与哪一个超类的 op 捆绑。 define type OT 3 supertypes OT 1,OT 2 is … operations refine op … implementation define op … self. OT 1 $op( … );!! 执行 OT 1 中定义 op 方法 end type OT 3 ;
多重继承带来的冲突二 -- 类型冲突 类型冲突是更严重的且目前无法消解的 问题 –(1) 属性类型冲突 : 从不同的父类分别继承了 一个同名但类型不同的属性 如 OT 1.A 与 OT 2.A 类型不相容 在 OT 3 中对同名不相容的属性错误无法在静态编 译时被检查出, 只有在运行时才能出现。
– ( 2 )操作类型冲突 从不同的父类分别继承了一个同名但返回类型不同的操作。 Delcare f : OT 1 || … - > T 1 Delcare f : OT 2 || … - > T 2 对于一个具有 OT 2 类型的变量 O 2 有一个引用 链 :O 2.f( … ).q( … ), 即函数 q 将由一个 T 2 类型的实例引用。 而函数程序运行时, 当 O 2 指向一个 OT 3 实例时,函数 f 根据 优先队列解决方法将选择 OT 1 的方法,其返回一个 T 1 类型 实例 -- 与 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 him meetingParticipants.insert(developmentDep); !! they will send a representation foreach (emp in meetingParticipants) print (emp.name);