Skip to content

组合、聚合与继承


正确选择用户定义类型中的组合、聚合和继承。

前言:

复杂的类型结构可以通过组合、聚合和继承来构建。

组合或聚合(关联的特殊形式)是从简单类型创建更复杂类型的过程,而继承是通过获取现有类型的属性和行为来创建更复杂类型的过程。

有一个非常著名的设计原则:"优先使用组合而非继承",也可以说"不要仅仅为了代码复用而使用继承"。

FreeBASIC 中(以及许多其他语言中)优先使用组合的另一个原因是它不支持多重继承。

组合、聚合、继承的定义

选择使用哪种关系类型时,需要回答的正确问题是:在应用程序的整个生命周期内,这种关系更接近于 "HAS-A"(拥有)"USES-A"(使用) 还是 "IS-A"(是)(见下方示例)。

当用户需要使用某个类型的属性和行为而不在其类型内部修改它时,关联是更好的选择。

而如果需要在用户类型内部使用并修改某个类型的属性和行为,则最好使用继承。

各关系的定义

组合(强关联)在对象之间建立 "HAS-A" 关系:

  • 在组合中,组件对象仅为其复合对象而存在。

  • 如果复合对象被销毁,组件对象也会被销毁:

因为类型本身包含了该对象。

  • type 中为 object 的组合添加成员字段的语法(或类似语法):

objectname As objecttypename [= initializer]

聚合(弱关联)在对象之间建立 "USES-A" 关系:

  • 在聚合中,组件对象可以独立于其在聚合对象中的使用而存在。

  • 销毁聚合对象不会销毁组件对象:

因为类型只包含指向该对象的指针。

  • type 中为 object 的聚合添加成员字段的语法(或类似语法):

objectptrname As objecttypename Ptr [= initializer]

继承在可实例化的类型之间建立 "IS-A" 关系:

  • 派生类型具有与其基类型相同的特征和功能,并有所扩展。

  • type 声明头部用于 object 继承的语法:

Type typename [Alias alternatename] Extends objecttypename [Field = alignment]

对象关联(组合/聚合)相对于继承的优点

在大多数情况下,"HAS-A""USES-A" 关系在语义上比 "IS-A" 关系更正确。

聚合比继承更灵活。

类型的实现可以在运行时通过更改组件对象来改变,但继承无法做到这一点(基类型的行为在运行时无法更改)。

基于对象关联的设计通常会有更少的类型。

可以通过将多个对象组合为一个来实现伪"多重继承"(在不支持多重继承的语言中)。

不会有过程/属性名称之间的冲突,而继承可能会发生这种情况。

对象关联(组合/聚合)相对于继承的缺点

使用聚合时,系统的行为可能更难仅通过查看源代码来理解,因为它更动态,类型之间在运行时的交互更多,而不是在编译时。

关联方法可能需要更多的代码和时间投入。

基于对象关联的设计通常会有更多的对象。

示例

混合使用组合、聚合和继承的简单示意性示例:

start GeSHi

vb
' Between the different types "Driver", "Person", "Driver_license" and "Vehicle", the respective relationships are:
'    - A driver "IS-A" person (driver is a person): => "INHERITANCE".
'    - A driver "HAS-A" driver's license (driver license only existing for the driver): => "COMPOSITION".
'    - A driver "USES-A" vehicle (vehicle lifetime independent of the driver life): => "AGGREGATION".

Type Person
    Public:
        Dim As String full_name
        Declare Constructor (ByRef _full_name As String)
    Protected:  '' to forbid at compile time the default-construction attempt of a Person instance
        Declare Constructor ()
End Type
Constructor Person (ByRef _full_name As String)
    This.full_name = _full_name
End Constructor

Type Driver_license
    Public:
        Dim As Integer number
End Type

Type Vehicle
    Public:
        Dim As String registration
        Declare Constructor (ByRef _registration As String)
End Type
Constructor Vehicle (ByRef _registration As String)
    This.registration = _registration
End Constructor

Type Driver Extends Person        '' inheritance
    Public:
        Dim As Driver_license dl  '' composition
        Dim As Vehicle Ptr pv     '' aggregation
        Declare Constructor (ByRef _full_name As String, ByRef _dl As Driver_license)
End Type
Constructor Driver (ByRef _full_name As String, ByRef _dl As Driver_license)
    Base(_full_name)
    This.dl = _dl
End Constructor

Dim As Driver d1 = Driver("User fxm", Type<Driver_license>(123456789))

Dim As Vehicle Ptr pv1 = New Vehicle("ABCDEFGHI")
d1.pv = pv1

Print "Person full name      : " & d1.full_name
Print "Driver license number : " & d1.dl.number
Print "Vehicle registration  : " & d1.pv->registration

Delete pv1
d1.pv = 0

Sleep

end GeSHi

输出:

Person full name      : User fxm
Driver license number : 123456789
Vehicle registration  : ABCDEFGHI

参见

返回 目录

基于 FreeBASIC 官方文档翻译 如有侵权请联系我们删除
FreeBASIC 是开源项目,与微软公司无隶属关系