OOP Fortran多态编程中是否必须保留带参抽象初始化接口?
Fortran抽象类初始化泛型绑定的设计问题解答
你参考实现的第一种固定3个实参的抽象init方案,确实存在适配代码冗余、参数扩展需要联动修改所有子类的问题,但你调整后的第二版无参数nopass空接口方案,只是钻了Fortran泛型绑定的语法空子,能正常编译但设计上完全不合理,有三个绕不开的核心问题:
- 你想要的「强制子类实现有效init方法」的约束是完全失效的。这套写法里编译器唯一强制要求子类实现的,是那个零参数、没有任何实际逻辑的空
shared_init过程,根本不会校验你实际给init泛型绑定的过程是否真的是初始化逻辑、参数是否匹配。哪怕你给某个子类的init绑定一个打印固定字符串的无关子程序,甚至完全忘了写实际初始化逻辑,编译器也不会抛出任何错误。和你直接去掉父类延迟绑定、让子类自行实现初始化的方案相比,除了多写三个无意义的空子程序,没有增加任何实际的约束效力。 - 完全丧失了多态设计的价值。父类
shape_t上定义的init泛型,实际只有那个空过程能通过父类多态引用调用,你在各个子类里扩展的不同参数的init重载,根本无法通过class(shape_t)类型的父类引用直接唤起。你自己写的示例主程序也能印证这一点:调用init之前必须先写select type做动态类型分支判断,和完全不用抽象父类、给每个形状类型单独写初始化子程序的写法相比,没有减少任何重复代码,也没获得多态调用的便利。 - 给后续维护埋下隐蔽的出错点。其他协作者看到抽象父类
shape_t定义了延迟绑定的init方法,第一反应会认为可以直接通过父类多态引用统一完成初始化,实际写代码时才会发现无法传入对应参数、调用直接报错,非常容易引入难排查的bug。
更合理的实现思路
Fortran语法本身不支持在抽象接口内直接定义重载的泛型过程,遇到不同子类初始化参数差异较大的场景,不用硬凑空过程骗过编译器检查,按实际业务场景选成熟方案即可:
- 需要通过父类引用统一初始化的场景:用多态配置类型做参数中转。
先定义一个抽象的配置基类,每个具体形状对应自己的配置子类存储专属初始化参数,父类的抽象init接口只接收这个配置基类的多态参数即可,核心实现逻辑参考:
每个子类实现type, abstract :: shape_config_t end type shape_config_t ! 每个形状对应专属配置类型 type, extends(shape_config_t) :: line_config_t real :: length end type line_config_t type, extends(shape_config_t) :: rectangle_config_t real :: length, width end type rectangle_config_t type, abstract :: shape_t contains procedure(abstract_init), deferred :: init procedure(abstract_print), deferred :: print_size end type shape_t abstract interface subroutine abstract_init(this, config) import shape_t, shape_config_t class(shape_t), intent(inout) :: this class(shape_config_t), intent(in) :: config end subroutine abstract_init subroutine abstract_print(this) import shape_t class(shape_t), intent(in) :: this end subroutine abstract_print end interfaceinit时,把传入的config参数转为自己对应的配置子类型读取参数即可。后续新增形状类型时,只需要新增对应的配置子类、实现对应init逻辑,不需要修改任何已有旧代码,同时编译器也会强制要求所有子类实现有效的init方法,没有冗余的适配代码。 - 不需要多态统一初始化的场景:直接删掉父类上凑数的init绑定。
如果你的代码逻辑和示例主程序一致,初始化操作之前本来就需要通过select type判断具体动态类型,完全没必要在父类硬加一个假的延迟绑定。直接给每个子类单独实现对应参数的init泛型即可,担心子类漏写init的问题,靠代码评审、单元测试覆盖就能完成约束,比写空接口自欺欺人靠谱得多。
内容的提问来源于stack exchange,提问作者Stef1611
相关产品推荐
相关产品推荐

