泛化关系是一般元素和具体元素之间的一种分类关系。具体元素与一般元素完全一致,但包含一些额外的信息。在允许使用一般元素的场合,可以使用具体元素的实例。
主题
泛化关系
在实际生活中,有许多东西都具有共同的特征。例如,狗和猫都是动物。对象也可以具有共同的特征,您可以使用它们所属类之间的泛化关系来阐明这些共同特征。通过将共同特征抽取到它们所属的类中,可以在将来更容易地变更和维护系统。
泛化关系表示一个类对另一个类的继承。继承而得的类称为后代。被继承的类称为祖先。继承意味着祖先的定义(包括任何特征,如属性、关系或对其对象执行的操作)对于后代的对象也是有效的。泛化关系是从后代类到其祖先类的关系。
泛化关系可以发生在几个阶段,这使您可以对复杂的、多级别的继承分层结构进行建模。一般特征位于继承分层结构的上部,而特殊特征处于较低的部分。也就是说,您可以使用泛化关系来对一般概念的特殊化进行建模。
示例
在回收机系统中,所有类(Can、Bottle 和 Crate)都描述了不同类型的贮藏物品。除了属于同一类型之外,它们具有两项共同特征:都有高度和重量。您可以通过一个单独的贮藏物品 (Deposit Item) 类中的属性和操作对这些特征进行建模。Can、Bottle 和 Crate 将继承此类的特征。
类 Can、Bottle 和 Crate 具有共同的特征:高度和重量。它们都是一般概念“贮藏物品”的特殊化。
多次继承
虽然类通常只从一个类中继承,但它也可以通过多次继承从若干个其他类中进行继承。
当使用多次继承时,必须注意以下两个潜在的问题:
- 如果某个类从若干个类中进行继承,您就必须检查祖先中关系、操作和属性的命名方式。如果在若干个祖先中出现了同一个名称,则必须说明这对于特定的继承所得类来说意味着什么,例如,通过限定名称来表明声明的来源。
- 如果使用重复继承,同一祖先就会被一个后代继承多次。当发生这种情况时,继承分层结构将表现为“菱形”,如下所示。
多次重复继承。Scrolling Window With Dialog Box 类正从 Window 类中进行多次继承。
在这种环境下可能产生一个问题,即有多少 Window 属性的副本包含在 Scrolling Window With Dialog Box 的实例中?因此,如果您要使用重复继承,就必须对继承的语义进行明确定义;在大多数情况下,它都通过支持多次继承的编程语言来定义。
通常,多次继承的编程语言规则比较复杂,并且经常难以正确使用。因此,最好在需要时才使用多次继承,使用时应谨慎小心。
抽象类和具体类
如果某个类未被实例化,并且只是为了让其他类继承它而存在,那么它就是抽象类。而实际上被实例化的类则是具体类。请注意,抽象类要成为有用的类,必须至少有一个后代。
示例
库房管理系统中的 Pallet Place 是抽象的实体类,它代表各种不同类型的托盘位置所共有的特征。该类由具体类 Station、Transporter 和 Storage Unit 来继承,所有这些具体类都可以作为库房中的托盘位置。所有这些对象都具有一个共同的特征:它们可以容纳一个或多个 Pallet(托盘)。
被继承的类(此处为 Pallet Place)是抽象的,它本身并不进行实例化。
使用
因为各个类构造型具有不同的目的,所以从一个类构造型到另一个类构造型的继承并没有什么意义。例如,如果让边界类继承实体类,边界类就会成为某种混合的东西。因此,只应在具有相同构造型的类之间使用泛化关系。
您可以使用泛化关系来表述类之间的两种关系:
- 建立子类型,指定后代是祖先的子类型。建立子类型意味着后代继承祖先的结构和行为,并且后代是祖先的一种类型(也就是说,后代是一种在任何情况下都可以替代其所有祖先的子类型)。
- 建立子类,指定后代是祖先的一个子类(而不是子类型)。建立子类意味着后代继承祖先的结构和行为,并且后代不是祖先的一种类型。
要创建以上两种关系,可以抽取若干个类所共有的特征,然后将其放入一个单独的类中供其他类继承;或者创建使一般类特殊化的新类,并让它们从这些一般类中进行继承。
如果两个变量正好一致,您就应该可以毫无困难地在类与类之间建立正确的继承关系。但在某些情况下,它们并不一致,此时就必须使继承具有可以理解的使用目的。至少,您应知道模型中每种继承关系的目的。
用于支持多态性的继承
建立子类型意味着后代是一种可以在任何情况下替代其所有祖先的子类型。建立子类型是多态性的特殊情况和重要的特征,因为它允许您对所有客户(使用祖先的对象)进行设计,而不必考虑祖先的潜在后代。这使客户对象更具一般性,因而可以复用。当客户使用实际对象时,它将以一种特定的方式工作,并且总是会发现该对象在执行其任务。建立子类型可确保系统容许对子类型集进行变更。
示例
在库房管理系统中,Transporter Interface 类用所有类型的运输设备(如起重机和卡车)来定义基本的交通功能。该类还定义了操作 executeTransport。
Truck Interface 类和 Crane Interface 类都从 Transporter Interface 中进行继承;所以这两个类的对象将对消息 executeTransport 作出响应。这些对象可以随时替代 Transporter Interface,并将提供它的所有行为。因此,其他对象(客户对象)可以向 Transporter Interface 对象发送消息,而不必知道 Truck Interface 或 Crane Interface 对象是否会对消息作出响应。
Transporter Interface 类甚至可以是抽象的,它本身永远不会进行实例化。在这种情况下,Transporter Interface 只可以定义 executeTransport 操作的签名,而由后代类来实施它。
某些面向对象的语言(如 C++)将类分层结构用作类型分层结构,这使得设计人员必须使用继承在设计模型中建立子类型。而其他语言(如 Smalltalk-80)在编译时不进行类型检查。如果对象无法对已接收的消息作出响应,它们就会产生错误消息。
在不进行类型检查的语言中,最好使用泛化关系表明子类型关系。在某些情况下,您应利用泛化关系使对象模型和源代码更易于理解和维护,而无论编程语言是否允许这样。继承的这种使用方式是否是有效,主要取决于编程语言的约定。
用于支持实施复用的继承
通过建立子类,能够使泛化关系可以复用。当建立子类时,要考虑通过继承由其他类定义的特征,可以复用实施的哪些部分。当实施特定的类时,可以通过建立子类来复用代码,从而减少了工作量。
示例
在 Smalltalk-80 类库中,类 Dictionary 继承了 Set 的特征。
此泛化关系存在的原因是,Dictionary 可以复用 Set 的实施中的某些一般方法和存储策略。即使可以将 Dictionary 当作 Set(包含键值对),Dictionary 也不是 Set 的子类型,因为您无法在 Dictionary 中添加任何类型的对象(只能添加键值对)。使用 Dictionary 的对象并未意识到它实际上是 Set。
建立子类通常会导致不合逻辑的继承分层结构,这种结构一般都难于理解和维护。因此,最好不要只为实现复用而使用继承,除非在编程语言使用方面另有建议。这种复用通常都难于维护。类 Set 的任何变更都可能意味着继承类 Set 的所有类都会发生大的变更。应记住这一点,并只从稳定的类中进行继承。继承实际上会冻结类 Set 的实施,因为它的变更代价过于昂贵。
编程语言中的继承
如何在设计中使用泛化关系,这应主要取决于继承在编程语言中的语义和建议用法。面向对象的语言支持类之间的继承关系,但非面向对象的语言无此支持。您应在设计模型中处理语言特征。如果您使用的是不支持继承或不支持多次继承的语言,就必须在实施中模拟继承。在这种情况下,最好在设计模型中对模拟进行建模,而不是使用泛化关系来描述继承结构。如果使用泛化关系建立继承结构的模型,然后在实施中模拟继承,就会使设计前功尽弃。
如果您使用的是不支持继承或不支持多次继承的语言,就必须在实施中模拟继承。在这种情况下,最好在设计模型中对模拟进行建模,而不是使用泛化关系来描述继承结构。如果使用泛化关系来建立继承结构的模型,然后在实施中模拟继承,就会使设计前功尽弃。
您可能需要在模拟期间更改接口和其他对象特征。建议您通过下列方法之一来模拟继承:
- 让后代向祖先转发消息。
- 在每个后代中复制祖先的代码。在这种情况下,将不会创建任何祖先类。
示例
在此示例中,后代通过作为关联关系实例的链接向祖先转发消息。
将 Can、Bottle 和 Crate 对象所共有的行为分配给一个特殊的类。共同具有此行为的对象向贮藏物品对象发送消息,以便在必要时执行此行为。
今天的文章泛化关系_类与类之间的关系泛化分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/49752.html