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 ...
随机推荐
- GPS地图生成03之数据获取
1. 引言¶ 六只脚是国内著名的户外网站,拥有大量的户外GPS轨迹路线,网址为:http://www.foooooot.com/ 2. 数据分析¶ 2.1 获取所有轨迹¶ 搜索关键 ...
- 【故障公告】Kubernetes 集群节点宕机造成博客站点故障
非常抱歉!今天 18:40-18:55 左右 Kubernetes 集群一台高配节点突然宕机,造成博客站点故障,访问时出现 502 Bad Gateway,由此给您带来麻烦麻烦,请您谅解. 发现故障并 ...
- ChatGPT Java客户端,OpenAi的Java版本SDK已完成,请火速接入。
已经支持OpenAI官方的全部api,有bug欢迎朋友们指出,互相学习. 源码地址:https://github.com/Grt1228/chatgpt-java 不对之处欢迎指正. 注意:由于这个接 ...
- Anaconda 环境中安装OpenCV (cv2)
1.使用Anaconda 的对应环境,查看环境中的Python版本号 (1)使用Anaconda 查看存在的环境:conda info --env (2)激活环境:conda activate XXX ...
- .net core使用 ELK
一 Linux 下安装部署 第一种方法:docker-compose 安装方式 1.1 创建 docker-compose.yml 文件 version: '3.1' services: elasti ...
- Servlet简介和ServletContext
0x01: 什么是Servlet? 是sun公司开发动态web的技术 实现了servlet接口的Java程序 0x02: Servlet的实现类有哪些? Servlet接口默认有两个实现类 HttpS ...
- 【剑指Offer】【字符串】字符串的排列
题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入一个字符串,长 ...
- 四点DLT (Direct Linear Transformation) 算法
\(\mathrm{x}_{i}\) 表示变化前的齐次坐标 \(\mathbf{x}_{i}^{\prime}\) 表示变化后的齐次坐标 我们需要求到一个 \(3\times3\) 的变换矩阵 \(\ ...
- 蓝牙mesh组网实践(手机配网例程配合wch mesh手机app的使用)
目录 CH582的官方EVT在22年7月更新后,修改了手机配网代码以支持wch mesh手机app.使用该app可以保存手动配网信息,设置订阅地址分组,OTA升级,极大方便了用户管理节点. 老版本的E ...
- vue 滑动到指定位置
在Vue中,有三种方式可以实现H5页面滑动至指定位置 方法1: //先获取目标位置距离 mounted() { this.$nextTick(() => { setTimeout(() => ...