• 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) ])。

  • 相关阅读:
    #3.14 Piday#我的圆周率日
    FUI- 我离钢铁侠还差几步?
    POJ 3617 Best Cow Line (贪心)
    POJ 2386 Lake Counting (水题,DFS)
    POJ 1852 Ants (等价思考)
    CCF 201403-3 命令行选项 (STL模拟)
    CCF 201403-2 窗口 (STL模拟)
    CCF 201403-1 相反数 (水题)
    CCF 201312-4 有趣的数 (数位DP, 状压DP, 组合数学+暴力枚举, 推公式, 矩阵快速幂)
    CCF 201312-3 最大的矩形 (暴力,离散化)
  • 原文地址:https://www.cnblogs.com/jiangleads/p/16095187.html
Copyright © 2020-2023  润新知