230 likes | 410 Views
第十三章 多重继承和多重替换 Multiple Inheritance and Multiple Substitutability. 多重继承 — 当一个子类从一个以上的超类型继承属性和方法 多从替代 — 不同类型的对象之间的相互替代. 多重继承. 现实世界的复杂性 , 由于一个实体的多哥角色性 , 在进行泛化抽象时 , 常常需要从不同角度进行。反之 , 一个实体可以从不同继承特征 , 构成多重继承。 如图 OT 3 继承了 OT 1 和 OT 2 的全部特征(属性 , 操作) OT 3 是 OT 1 和 OT 2 的一个类型扩展
E N D
第十三章 多重继承和多重替换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,OT2is … 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 viaself.knife; fashion Scissor viaself.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);