泛化
泛化关系是类元的一般描述和具体描述之间的关系,具体描述建立在一般描述的基础之上,并对其进行了扩展。具体描述与一般描述完全一致所有特性、成员和关系,并且包含补充的信息。例如,抵押是借贷中具体的一种,抵押保持了借贷的基本特性并且加入了附加的特性,如房子可以作为借贷的一种抵押品。一般描述被称作父,具体描述被称作子如借贷是父而抵押则是子。泛化在类元(类、接口、数据类型、用例、参与者、信号等等)、包、状态机和其他元素中使用。在类中,术语超类和子类代表父和子。
泛化用从子指向父的箭头表示,指向父的是一个空三角形(如 图 4 – 7 表示)。多个泛化关系可以用箭头线组成的树来表示,每一个分支指向一个子类。
图 4–7 泛化表示法
泛化的用途
泛化有两个用途。第一个用途是用来定义下列情况:当一个变量(如参数或过程变量)被声明承载某个给定类的值时,可使用类(或其他元素)的实例作为值,这被称作可替代性原则(由
Barbara Liskov 提出)。该原则表明无论何时祖先被声明了,则后代的一个实例可以被使用。例如,如果一个变量被声明拥有借贷,那么一个抵押对象就是一个合法的值。
泛化使得多态操作成为可能,即操作的实现是由它们所使用的对象的类,而不是由调用者确定的。这是因为一个父类可以有许多子类,每个子类都可实现定义在类整体集中的同一操作的不同变体。例如,在抵押和汽车借贷上计算利息会有所不同,它们中的每一个都是父类借贷中计算利息的变形。一个变量被声明拥有父类,接着任何子类的一个对象可以被使用,并且它们中的任何一个都有着自己独特的操作。这一点特别有用,因为在不需要改变现有多态调用的情况下就可以加入新的类。例如,一种新的借贷可被新增加进来,而现存的用来计算利息操作的代码仍然可用。一个多态操作可在父类中声明但无实现,其后代类需补充该操作的实现。这种不完整操作是抽象的(其名称用斜体表示)。
泛化的另一个用途是在共享祖先所定义的成分的前提下允许它自身定义增加的描述,这被称作继承。继承是一种机制,通过该机制类的对象的描述从类及其祖先的声明部分聚集起来。继承允许描述的共享部分只被声明一次而可以被许多类所共享,而不是在每个类中重复声明并使用它,这种共享机制减小了模型的规模。更重要的是,它减少了为了模型的更新而必须做的改变和意外的前后定义不一致。对于其他成分,如状态、信号和用例,继承通过相似的方法起作用。
· 继承
每一种泛化元素都有一组继承特性。对于任何模型元素的包括约束。对类元而言,它们同样包括一些特性(如属性、操作和信号接收)和关联中的参与者。一个子类继承了它的所有祖先的可继承的特性。它的完整特性包括继承特性和直接声明的特性。
对类元而言,没有具有相同特征标记的属性会被多次声明,无论直接的或继承的,否则将发生冲突,且模型形式错误。换而言之,祖先声明过的属性不能被后代再次声明。如果类的接口一致(具有同样的参数、约束和含义),操作可在多个类中声明。附加的声明是多余的。一个方法在层次结构中可以被多个类声明,附在后代上的方法替代(重载)在任何祖先中声明过的具有相同特征标记的方法。如果一个方法的两个或多个副本被一个类继承(通过不同类的多重继承),那么它们会发生冲突并且模型形式错误(一些编程语言允许显式选定其中的一种方法。我们发现如果在后代类中重新定义方法会更简单、安全)。元素中的约束是元素本身及它所有祖先的约束的联合体,如果它们存在不一致,那么模型形式错误。
在一个具体的类中,每一个继承或声明的操作都必须有一个已定义的方法,无论是直接定义或从祖先那里继承而来的。
· 多重继承
如果一个类元有多个父类,那么它从每一父类那里都可得到继承信息(如图 4-8 )。它的特征(属性、操作和信号)是它的所有父类特征的联合。如果同一个类作为父类出现在多条路径上,那么它的每一个成员中只有它的一个拷贝。如果有着同样特征的特性被两个类声明,而这两个类不是从同一祖先那里继承来的(即独立声明),那么声明会发生冲突并且模型形式错误。因为经验告诉我们设计者应自行解决这个问题,所以
UML 不提供这种情形的冲突解决方案。像 Eiffel 这样的语言允许冲突被程序设计者明确地解决,这比隐式的冲突解决原则要安全,而这些原则经常使开发者大吃一惊。
图 4–8 多重继承
· 单分类和多重分类
在最简单的形式中,一个对象仅属于一个类,许多面向对象的语言有这种限制。一个对象仅属于一个类并没有逻辑上的必要性,我们只要从多个角度同时观察一下真实世界的对象就可以发现这一点。在
UML 更概括的形式中,一个对象可以有一个或多个类。对象看起来就好像它属于一个隐式类,而这个类是每个直接父类的子类
— 多重继承可以免去再声明一个新类,这可提高效率。
· 静态与动态类元
在最简单的形式中,一个对象在被创建后不能改变它的类。我们再次说明,这种限制并没有逻辑上的必要性,而是最初目地是使面向对象编程语言的实现更容易些。在更普遍的形式下,一个对象可以动态改变它的类,这么做会得到或失去一些属性或关联。如果对象失去了它们,那么在它们中的信息也就失去了并且过后也不能被恢复,哪怕这个对象变回了原来的类。如果这个对象得到了属性或关联,那么它们必须在改变时就初始化,就像初始化一个新对象一样。
当多重分类和动态分类一起使用时,一个对象就可以在它的生命期内得到或失去类。动态类有时被称作角色或类型。一个常见的建模模式是每个对象有一个唯一的静态的固有类(即不能在对象的生命期内改变的类),加上零个或多个可以在对象生命期内加入或移走的角色类。固有类描述了这个对象的基本特性,而角色类描述了暂时的特性。虽然许多程序设计语言不支持类声明中的多重动态分类,然而它仍然是一个很有用的建模概念,并且可以被映射到关联上。
|