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

  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/jiangleads/p/16095187.html
Copyright © 2020-2023  润新知