组合、聚合与继承
- 来源: https://www.freebasic.net/wiki/wikka.php?wakka=ProPgCompoAggregInherit
- 最后更新: 2020-08-07
正确选择用户定义类型中的组合、聚合和继承。
前言:
复杂的类型结构可以通过组合、聚合和继承来构建。
组合或聚合(关联的特殊形式)是从简单类型创建更复杂类型的过程,而继承是通过获取现有类型的属性和行为来创建更复杂类型的过程。
有一个非常著名的设计原则:"优先使用组合而非继承",也可以说"不要仅仅为了代码复用而使用继承"。
在 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
' 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
Sleepend GeSHi
输出:
Person full name : User fxm
Driver license number : 123456789
Vehicle registration : ABCDEFGHI参见
Type (UDT)Extends- 继承多态
- OBJECT 内置和 RTTI 信息
返回 目录