设计使用Fortran相互交互的对象的建议

cri*_*nja 7 oop fortran

我整个下午一直在试着找出这个问题,一直在撞墙,所以我希望有人可以帮助我.

我有一个抽象的基类,叫做base_model(比如说),它在Fortran2003中看起来像:

type, abstract :: base_model
contains
  procedure(initMe), pass(this), deferred :: init ! constructor
  procedure(delMe), pass(this), deferred :: delete ! destructor
  procedure(solveMe), pass(this), deferred :: solve
end type base_model
Run Code Online (Sandbox Code Playgroud)

其中,显然是抽象过程initMe,delMesolveMe使用抽象接口块定义.然后我有三个派生类,叫做model1,model2model3(说):

type, extends(base_model) :: model1
  double precision :: x,y
contains
  procedure :: init => init_model1
  procedure :: delete => delete_model1
  procedure :: solve => solve_model1
end type model1

type, extends(base_model) :: model2
contains
  procedure :: init => init_model2
  procedure :: delete => delete_model2
  procedure :: solve => solve_model2
end type model2

type, extends(base_model) :: model3
contains
  procedure :: init => init_model3
  procedure :: delete => delete_model3
  procedure :: solve => solve_model3
end type model3
Run Code Online (Sandbox Code Playgroud)

然后,我有一个"控制"对象,称为control(说),它扩展了一个摘要base_control:

type, abstract :: base_control
  class(base_model), allocatable :: m1
  class(base_model), allocatable :: m2
  class(base_model), allocatable :: m3
contains
  procedure(initMe), pass(self), deferred :: init
  procedure(delMe), pass(self), deferred :: delete
  procedure(runMe), pass(self), deferred :: run
end type base_control

type, extends(base_control) :: control
contains
  procedure :: init => init_control
  procedure :: delete => delete_control
  procedure :: run => run_control
end type control
Run Code Online (Sandbox Code Playgroud)

的对象m1,m2并且m3可以被分配到任何模型:model1,model2model3,和在这取决于哪个的"控制"是由用户请求的任何特定顺序"解决".

三个可分配对象(m1,m2m3)需要在它们之间传递数据.鉴于它们是"控制"对象的成员,我可以为每个模型定义一个"getter",然后将所需的数据传递到每个模型中.但是,特定模型在编译时是未知的,因此,"控制"对象不知道要获取什么数据,实际上,模型不知道要接收哪些数据!

例如,如果我allocate(model1::m1)(即,将m1分配为类型model1)则它将包含两位数据double precision :: x,y.然后,如果m2被分配为type model2(allocate(model2::m2)),它可能需要x但如果它被分配为type model3(allocate(model3::m2)),那么它可能需要y来自m1.因此,考虑到"控制"对象无法知道什么类型的m2分配是,它如何能得到必要的数据m1传递到m2

另一个复杂因素是模型之间的相互作用通常是循环的.也就是说,m1需要来自的数据m2,m2需要来自m1等等的数据.而且,所需的数据通常不仅特定于模型,而且在类型和数量上也是可变的.

不幸的是,数据xy不的成员base_model,因此,传递m1m2作为参数就不能工作.

所以我有以下问题:

  1. 有没有更好的方法来设计这些对象,以便我可以轻松地在它们之间传递数据?在这里展望,有一些建议,最好的办法是重新设计对象,使它们之间的交互不是循环的.但是,这是必要的!

  2. 我是否必须为可能在对象之间共享的每个数据写一个"getter" ?这似乎是很多编码(我有很多可能共享的数据).然而,这似乎也相当复杂,因为"getter"(特定于一条数据)也必须满足抽象接口.

在像Python这样的高级语言中,这很简单,因为我们可以简单地创建一个新的数据类型作为模型的组合,但据我所知,这在Fortran中是不可能的.

提前致谢.任何帮助是极大的赞赏.

编辑:在下面与francescalus讨论后,select type是一个选项.实际上,在上面给出的简单例子中,select type将是一个不错的选择.但是,在我的实际代码中,这将导致大的嵌套select types,所以如果有一种方法可以不使用select type我希望它.感谢francescalus指出我的错误select type.

Jos*_*rra 3

回答你的两个问题:

有没有更好的设计方法?

我不太清楚为什么你的设计中有这么多限制,但总之是的。您可以为您的模型使用上下文管理器。我建议您查看这个答案:上下文类模式

是否必须为每个模型编写 getter 方法?

不完全是这样,如果您在这个特定问题上使用上下文策略,那么您唯一需要在每个模型上实现的就是一个在模型之间共享数据的 setter 方法。

我用 Python 实现了这个场景的可行解决方案。代码胜于雄辩。我避免使用 Python 的任何特殊功能,以便让您清楚地了解如何在这种情况下使用上下文。

from abc import ABC, abstractmethod
import random

class BaseModel(ABC):
    def __init__(self, ctx):
        super().__init__()
        self.ctx = ctx
        print("BaseModel initializing with context id:", ctx.getId())

    @abstractmethod
    def solveMe():
        pass

class BaseControl(object):
    # m1 - m3 could be replaced here with *args
    def __init__(self, m1, m2, m3):
        super().__init__()
        self.models = [m1, m2, m3]


class Control(BaseControl):
    def __init__(self, m1, m2, m3):
        super().__init__(m1, m2, m3)

    def run(self):
        print("Now Solving..")
        for m in self.models:
            print("Class: {} reports value: {}".format(type(m).__name__, m.solveMe()))


class Model1(BaseModel):
    def __init__(self, x, y, ctx):
        super().__init__(ctx)
        self.x = x
        self.y = y
        ctx.setVal("x", x)
        ctx.setVal("y", y)

    def solveMe(self):
        return self.x * self.y

class Model2(BaseModel):
    def __init__(self, z, ctx):
        super().__init__(ctx)
        self.z = z
        ctx.setVal("z", z)

    def solveMe(self):
        return self.z * self.ctx.getVal("x")

class Model3(BaseModel):
    def __init__(self, z, ctx):
        super().__init__(ctx)
        self.z = z
        ctx.setVal("z", z)

    def solveMe(self):
        return self.z * self.ctx.getVal("y")

class Context(object):
    def __init__(self):
        self.modelData = {}
        self.ctxId = random.getrandbits(32)

    def getVal(self, key):
        return self.modelData[key]

    def setVal(self, key, val):
        self.modelData[key] = val

    def getId(self):
        return self.ctxId


ctx = Context()

m1 = Model1(1,2, ctx)
m2 = Model2(4, ctx)
m3 = Model3(6, ctx)

# note that the order in the arguments to control defines behavior
control = Control(m1, m2, m3)
control.run()
Run Code Online (Sandbox Code Playgroud)

输出

python context.py
BaseModel initializing with context id: 1236512420
BaseModel initializing with context id: 1236512420
BaseModel initializing with context id: 1236512420
Now Solving..
Class: Model1 reports value: 2
Class: Model2 reports value: 4
Class: Model3 reports value: 12
Run Code Online (Sandbox Code Playgroud)

解释

简而言之,我们创建一个上下文类,它有一个可以在不同模型之间共享的字典。此实现非常特定于您提供的原始数据类型(IE x、y、z)。如果您需要在模型之间共享数据之前计算数据,您仍然可以通过solveMe()用延迟承诺替换返回来使用此模式。