Fortran 笔记之 继承和聚合
继承(类扩展)和聚合
参考自Introduction to Modern Fortran for the Earth System Sciences
我们在3.3部分的开头提到过,OOP范式通常会导致类型的层次结构。Fortran程序员可以使用两种机制来构造这些层次结构:继承和聚合。我们将在本节中简要讨论这些问题。
作为一个简单的展示示例,我们将了解如何从上一节(Vec2d)扩展DT,以表示3D向量。
继承
实现类型层次结构的第一种机制是继承(inheritance)(在Fortran标准中称为“类型扩展”)。这通过表达由类型建模的实体之间的“是”类型关系来实现代码重用。例如,在ESS中的植被模型中,我们可以定义一种类型的plant,以收集与所有plant类型的模型相关的属性(例如反照率)。然后可以为tree, grass等实现专门的类型,它们继承了植物的基本特征,但也添加了一些自己的特征(使用Fortran术语,我们说tree扩展了plant)。我们还说,plant是tree的parent/ancestor类型(或者,相当于,该tree是植物的chlid/descendant)。当然,这种专门化过程可以继续下去(通过为不同种类的树木创建子类等),尽管建议不要让层次结构太“高”(即有太多的继承层次)。
回到我们的简单例子,我们使用继承来定义Vec3d:
1 ! File: dt_composition_inheritance.f90
2 ! Purpose: Demonstrate how more complex types can be constructed using
3 ! inheritance (="type-extension" in Fortran); specifically, we look at
4 ! how a 'Vec3d'-type may be constructed from a 'Vec2d'-type.
5
6 module Vec2d_class
7 implicit none
8 private
9
10 type, public :: Vec2d ! DT explicitly declared "public"
11 private ! Make internal data "private" by default.
12 real :: mU = 0., mV = 0.
13 contains
14 private ! Make methods "private" by default.
15 procedure, public :: getMagnitude => getMagnitudeVec2d
16 procedure, public :: getU => getUVec2d
17 procedure, public :: getV => getVVec2d
18 end type Vec2d
19
20 ! Generic IFACE, for type-overloading
21 ! (to implement user-defined CTOR)
22 interface Vec2d
23 module procedure createVec2d
24 end interface Vec2d
25
26 contains
27 type(Vec2d) function createVec2d( u, v ) ! CTOR
28 real, intent(in) :: u, v
29 createVec2d%mU = u
30 createVec2d%mV = v
31 end function createVec2d
32
33 real function getMagnitudeVec2d( this ) result(mag)
34 class(Vec2d), intent(in) :: this
35 mag = sqrt( this%mU**2 + this%mV**2 )
36 end function getMagnitudeVec2d
37
38 real function getUVec2d( this ) ! Accessor-method (GETter).
39 class(Vec2d), intent(in) :: this
40 getUVec2d = this%mU ! Direct-access IS allowed here.
41 end function getUVec2d
42
43 real function getVVec2d( this ) ! Accessor-method (GETter).
44 class(Vec2d), intent(in) :: this
45 getVVec2d = this%mV ! Direct-access IS allowed here.
46 end function getVVec2d
47 end module Vec2d_class
48
49 module Vec3d_class
50 use Vec2d_class
51 implicit none
52 private
53
54 type, public, extends(Vec2d) :: Vec3d
55 private
56 real :: mW = 0.
57 contains
58 private
59 procedure, public :: getW => getWVec3d ! 读取w分量的方法
60 procedure, public :: getMagnitude => getMagnitudeVec3d !此处覆盖了Vec2d中的同名方法
61 end type Vec3d
62
63 interface Vec3d ! 定义用户构造器接口
64 module procedure createVec3d
65 end interface Vec3d
66
67 contains
68 ! 子类的自定义构造函数 Custom CTOR for the child-type.
69 type(Vec3d) function createVec3d( u, v, w )
70 real, intent(in) :: u, v, w
71 createVec3d%Vec2d = Vec2d( u, v) ! Call CTOR of parent.
72 createVec3d%mW = w
73 end function createVec3d
74
75 ! 覆盖父类方法 Override method of parent-type.
76 ! (to compute magnitude, considering 'w' too)
77 real function getMagnitudeVec3d( this ) result(mag)
78 class(Vec3d), intent(in) :: this ! 将方法与Vec3d类型绑定
79 ! this%Vec2d%getU() is equivalent, here, with this%getU()
80 mag = sqrt( this%Vec2d%getU()**2 + this%getV()**2 + this%mW**2 )
81 end function getMagnitudeVec3d
82
83 ! 专属于子类的方法 Method specific to the child-type.
84 ! (GETter for new component).
85 real function getWVec3d( this )
86 class(Vec3d), intent(in) :: this ! 将方法与Vec3d类型绑定
87 getWVec3d = this%mW
88 end function getWVec3d
89 end module Vec3d_class
90
91 program test_driver_inheritance
92 use Vec3d_class
93 implicit none
94 type(Vec3d) :: X
95
96 X = Vec3d( 1.0, 2.0, 3.0 )
97 write(*, '(4(a,f6.3))') "X%U = ", X%getU(), ", X%V = ", X%getV(), &
98 ", X%W = ", X%getW(), ", X%magnitude = ", X%getMagnitude()
99 end program test_driver_inheritance
Listing 3.34 src/Chapter3/dt_composition_inheritance.f90 (excerpt)
这是关于继承的几个值得注意的点:
- 父类型需要用 extends(<ParentTypeName>) 说明符来表示(第54行)。
- 继承会自动为子类型提供父类型的成员,以父类型命名。在我们的例子中,通过调用父级的自定义构造函数就可以清楚地看到这一点(第71行)。我们使用%来获得这个分量。因此,createVec3d%Vec2d(第71行)和this%Vec2d(第80行)本身就是Vec2d类型的对象。子类型还可以直接访问父类的公共数据和方法(在我们的例子中,没有public的数据,但我们在第80行通过继承的方法getU和getV可以访问数据)。
- 可以覆盖(override)父对象的方法(在子类型中)。我们使用它来定义getMagnitude的新版本(第75-81行),它正确地考虑了额外的分量w。但是,请注意,在覆盖时,方法的接口需要保持不变(除了传递对象的虚参的类型,它显然需要不同)。例如,我们不允许用一个函数重写getMagnitude,该函数接受两个参数而不是一个参数(假设我们需要)。
新的派生类型可以被调用,如下:
91 program test_driver_inheritance
92 use Vec3d_class
93 implicit none
94 type(Vec3d) :: X
95
96 X = Vec3d( 1.0, 2.0, 3.0 )
97 write(*, '(4(a,f6.3))') "X%U = ", X%getU(), ", X%V = ", X%getV(), &
98 ", X%W = ", X%getW(), ", X%magnitude = ", X%getMagnitude()
99 end program test_driver_inheritance
Listing 3.34 src/Chapter3/dt_composition_inheritance.f90 (excerpt)
在结束我们对继承的介绍时,请注意,在Fortran术语中,class关键字表示“类型的类(class of types)”(或继承层次结构(inheritance hierarchy))。这与其他OOP语言不同,“class”表示数据type(Fortran中的type)。此外,与其他语言不同,Fortran不允许多重继承(译者注:从多个父类继承?)(Metcalf等人. [Metcalf, M., Reid, J., Cohen, M.: Modern Fortran Explained. Oxford University Press, Oxford (2011)])。
聚合
实现类型层次结构的第二种机制是聚合(aggregation),它对类型之间的“has-A”关系进行建模,这对一些程序员来说可能更自然。我们还可以使用这种方法实现Vec3d类的另一个版本:
6 module Vec2d_class
7 implicit none
8 private
9
10 type, public :: Vec2d ! DT explicitly declared "public"
11 private ! Make internal data "private" by default.
12 real :: mU = 0., mV = 0.
13 contains
14 private ! Make methods "private" by default.
15 procedure, public :: getMagnitude => getMagnitudeVec2d
16 procedure, public :: getU => getUVec2d
17 procedure, public :: getV => getVVec2d
18 end type Vec2d
19
20 ! Generic IFACE, for type-overloading
21 ! (to implement user-defined CTOR)
22 interface Vec2d
23 module procedure createVec2d
24 end interface Vec2d
25
26 contains
27 type(Vec2d) function createVec2d( u, v ) ! CTOR
28 real, intent(in) :: u, v
29 createVec2d%mU = u
30 createVec2d%mV = v
31 end function createVec2d
32
33 real function getMagnitudeVec2d( this ) ! Method to compute magnitude.
34 class(Vec2d), intent(in) :: this
35 getMagnitudeVec2d = sqrt( this%mU**2 + this%mV**2 )
36 end function getMagnitudeVec2d
37
38 real function getUVec2d( this ) ! Accessor-method (GETter).
39 class(Vec2d), intent(in) :: this
40 getUVec2d = this%mU ! Direct-access IS allowed here.
41 end function getUVec2d
42
43 real function getVVec2d( this ) ! Accessor-method (GETter).
44 class(Vec2d), intent(in) :: this
45 getVVec2d = this%mV ! Direct-access IS allowed here.
46 end function getVVec2d
47 end module Vec2d_class
48
49 module Vec3d_class
50 use Vec2d_class
51 implicit none
52 private
53
54 type, public :: Vec3d
55 private
56 type(Vec2d) :: mVec2d ! DT-aggregation
57 real :: mW = 0.
58 contains
59 private
60 procedure, public :: getU => getUVec3d
61 procedure, public :: getV => getVVec3d
62 procedure, public :: getW => getWVec3d
63 procedure, public :: getMagnitude => getMagnitudeVec3d
64 end type Vec3d
65
66 interface Vec3d
67 module procedure createVec3d
68 end interface Vec3d
69
70 contains
71 ! 对于聚合类的用户构造函数 Custom CTOR for the aggregate-type.
72 type(Vec3d) function createVec3d( u, v, w )
73 real, intent(in) :: u, v, w
74 createVec3d%mVec2d = Vec2d( u, v ) ! 调用分量构造函数 Call CTOR of component.
75 createVec3d%mW = w
76 end function createVec3d
77
78 real function getMagnitudeVec3d( this )
79 class(Vec3d), intent(in) :: this
80 getMagnitudeVec3d = sqrt( this%getU()**2 + this%getV()**2 + this%mW**2 )
81 end function getMagnitudeVec3d
82
83 real function getUVec3d( this )
84 class(Vec3d), intent(in) :: this
85 getUVec3d = this%mVec2d%getU() ! 直接使用对象mVec2d的方法来获取U
86 end function getUVec3d
87
88 real function getVVec3d( this )
89 class(Vec3d), intent(in) :: this
90 getVVec3d = this%mVec2d%getV() ! 直接使用对象mVec2d的方法来获取V
91 end function getVVec3d
92
93 real function getWVec3d( this )
94 class(Vec3d), intent(in) :: this
95 getWVec3d = this%mW ! 定义获取w分量的方法
96 end function getWVec3d
97 end module Vec3d_class
98
99 program test_driver_aggregation
100 use Vec3d_class
101 implicit none
102 type(Vec3d) :: X
103
104 X = Vec3d( 1.0, 2.0, 3.0 )
105 write(*, '(4(a,f6.3))') "X%U = ", X%getU(), ", X%V = ", X%getV(), &
106 ", X%W = ", X%getW(), ", X%magnitude = ", X%getMagnitude()
107 end program test_driver_aggregation
Listing 3.36 src/Chapter3/dt_composition_aggregation.f90 (excerpt)
这只是简单地使用不太复杂的类型作为分量(第56行)。通常的访问控制机制指出,在Vec3d的实现中可以引用Vec2d的数据和方法(除了现在我们需要用分量名mVec2d来获得访问权限)。由于该实现没有其他显著的特性,我们在这里省略了对这些方法的讨论。
使用继承还是聚合
细心的读者可能会注意到,派生类型之间“is a”和“has a”关系的区别有时可能是主观的。实际上,按照我们前面的示例,基于这两种方法,相同类型的Vec3d使用相同的功能实现。这可能会使在实践中在两者之间进行选择变得混乱。一个粗略的经验法则是,如果问题中存在明显的类型层次结构,则使用继承,这将使子级对父方法的直接继承有益(无需重新实现它们,或定义“包装器方法(wrapper methods)”)。然而,如果子类经常需要重写父类的方法(或者更糟的是,如果父类的方法对子类没有意义!),聚合是首选的合成方法(见Rouson等人 [Rouson, D., Xia, J., Xu, X.: Scientific Software Design: The Object-OrientedWay. Cambridge University Press, Cambridge (2011) ])。
Fortran 笔记之 继承和聚合的更多相关文章
- c++学习笔记之继承篇
title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...
- pom.xml的继承、聚合与依赖
原文地址:https://my.oschina.net/zh119893/blog/232896 6.1 简介 pom.xml文件是Maven进行工作的主要配置文件.在这个文件中我们可以配置M ...
- Maven的继承和聚合
Maven的继承和聚合子项目的pom文件里通过<parent>节点来继承父项目 <parent> <groupId>com.tykj</groupId> ...
- maven中的继承和聚合的关系
maven中的继承和聚合的关系:两者的目的是不一样的,聚合的目的是能够快速的构建项目,继承的目的是减少重复配置.聚合:定义一个聚合模块,然后在pom文件中添加<module></mo ...
- pom文件继承与聚合
1.简介 pom.xml文件是Maven进行工作的主要配置文件.在这个文件中我们可以配置Maven项目的groupId.artifactId和version等Maven项目必须的元素:可以配置Mave ...
- Maven学习(八)继承和聚合
*聚合(多模块) 在一个项目中 往往有多个模块组成,例如有项目demo下面有a, b两个模块 为了能使用一条命令就能构建demo-a, demo-b两个模块, 需要创建一个额外的聚合模块, 然后通过该 ...
- maven之详解继承与聚合
说到聚合与继承我们都很熟悉,maven同样也具备这样的设计原则,下面我们来看一下Maven的pom如何进行聚合与继承的配置实现. 一.为什么要聚合? 随着技术的飞速发展和各类用户对软件的要求越来越高, ...
- Java基础笔记-抽象,继承,多态
抽象类: abstract修饰 抽象方法必须定义在抽象类中,抽象类不能创建对象. 在抽象方法中可以不定义抽象方法,作用是:让该类不能建立对象. 特点是: 1.定义在抽象类中 2.方法和类都用abstr ...
- Maven详解(八)------ 继承和聚合
1.继承 需求场景: 有三个 Maven 工程,每个工程都依赖某个 jar 包,比如 Junit,由于 test 范围的依赖不能传递,它必然会分散在每个工程中,而且每个工程的jar 包版本可能不一致. ...
- 【maven】依赖、继承、聚合
依赖: <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId&g ...
随机推荐
- chai 3D 之网格对象
推荐:将 NSDT场景编辑器 加入你的3D开发工具链 介绍 网格对象是由三角形和顶点组成的形状 在 CHAI3D 中,多边形网格是定义多面体对象形状的顶点和三角形的集合. 顶点是一个位置以及其 ...
- lama-cleaner全部命令行参数
usage: main.py [-h] [--host HOST] [--port PORT] [--config-installer] [--load-installer-config] [--in ...
- mogdb的一主两备
# 一.环境准备 |节点类别|主机名|IP||-|-|-||主节点|mogdb1 |192.168.3.68||备节点1|mogdb2 |192.168.3.69||备节点2|mogdb3|192.1 ...
- Android Studio连接SQLlite
1. MainActivity.java package com.example.dbproject;import android.database.sqlite.SQLiteDatabase;imp ...
- python 非阻塞线程对话框,非qt(解决qt MessageBox使用线程时候卡死问题)
def msg_okbox(self, strinfo, isYesno=False): if isYesno: return win32api.MessageBox(None, strinfo, & ...
- Git命令学习总结(廖雪峰官方Git教程)
1.Windows系统安装完Git后,需要在Git Bash命令窗口输入以下命令,进行用户名和邮箱设置.
- 2022-05-31内部群每日三题-清辉PMP
1.由于项目执行期间的范围变更,项目经理确定供应商必须对一个已在使用的产品模块进行更改.项目经理首先做什么? A.准备一份变更请求,以更新供应商的合同条款 B.检查采购管理计划和合同条款 C.将该信息 ...
- 蓝牙mesh组网实践(节点功能介绍)
目录 未配网设备在配好网,被纳入网络后,可称之为节点. 蓝牙mesh网络中,节点可以有选择地支持四大功能--朋友.低功耗.转发.代理功能,可以复用多个功能(由于单片机硬件限制,ch582除了复用转发功 ...
- Prometheus 特点
1.1 Prometheus的特点 Prometheus是一个开源的完整监控解决方案,其对传统监控系统的测试和告警模型进行了彻底的颠覆,形成了基于中央化的规则计算.统一分析和告警的新模型. 相比于传统 ...
- deepinlinux安装golang
Download 从 Google 官方获取安装包,不用FQ直接访问 http://golang.google.cn,选择 Linux 的安装包如 go1.14.linux-amd64.tar.gz ...