首页 > 解决方案 > 动态更改 Perl @ISA?

问题描述

我正在尝试动态合并旧版本的模块

我们目前有一个使用模块的类......这个问题非常简化

use Modulev1;

our @ISA = qw( Modulev1 );

sub new {
    my $class = shift or return
    my $self = $class::SUPER::new( @_ ) or return;

    $self->do_stuff();

    $self;

}

我们有一个旧模块,它具有相同的方法名称/变量,但远程服务器上不同版本的 api 的编码不同,我想“插入”作为选项。

use Modulev1;
use Modulev2;

our @ISA = qw( Modulev1 );

sub new {
    my $class = shift or return;
    my %args = @_;

    if( $args{ use_version_2 } ) {
        @ISA = qw( Modulev2 );
    }

    my $self = $class::SUPER::new( \%args ) or return;

    $self->do_stuff();

    $self;

}

这似乎以一种方式工作(获得结果),但感觉像是错误的方法,我收到一些警告,如“在 /opt/perl/lib/5.30.1/Exporter.pm 重新定义常量子例程模块::变量”这是有道理的,因为它试图在每个版本中定义两次这些常量,这感觉是错误的并且容易出现未来的错误。

所以有两个主要问题

1)像我一样重新分配@ISA有什么问题。

2)有没有更好的干净方法来尝试在代码中使用同一模块的 2 个版本,而不是这种丑陋的容易出错的黑客?感觉我应该以某种方式使模块更加独立,所以他们不能尝试重新定义常量之类的事情。

编辑:我也尝试过动态添加“使用模块”案例,但这似乎有同样的问题,并且感觉效率很低,因为这被调用了很多次。

编辑:为了澄清,脚本可能会在同一次运行中多次调用该对象,但使用不同的版本。

标签: perl

解决方案


像这样动态更改的主要问题@ISA是您正在将该更改应用于整个类,而不仅仅是使用use_version_2参数请求的一个实例。如果只期望该对象在程序中只使用一次(即它是一个单例),这可能无关紧要,但无论如何这都不是一个好习惯。另一个小问题是,无论何时@ISA更改,方法缓存都会重置(因为方法调用可能随后解析为不同的子例程)。

我会考虑的两个替代选项是为每个“版本”创建一个不同的子类,或者利用Role::TinyWith::Roles的角色,这使您可以通过本质上创建来动态地将一组行为应用于特定对象一个即时的子类。

例如,您可以使“Modulev1”和“Modulev2”成为两个角色,并应用请求的角色,这也将处理加载模块。

# first role module
package Modulev1;
use Role::Tiny;
# subroutines to be composed in
1;

# second role module
package Modulev2;
use Role::Tiny;
# subroutines to be composed in
1;

# user of roles
use With::Roles;
...
if ($args{use_version_2}) {
  $self->with::roles('Modulev2');
} else {
  $self->with::roles('Modulev1');
}

或者您可以创建两个单独的类,并将两个版本的公共代码放入他们将使用Role::Tiny::With静态使用的角色中(这类似于您在其他一些语言中使用“接口”的方式,除了它还可以提供实现)。

package ModuleCommon;
use Role::Tiny;
requires 'foo', 'bar'; # require these functions to be implemented by versioned class
sub baz { }
1;

package Modulev1;
use Role::Tiny::With;
sub foo { }
sub bar { }
with 'ModuleCommon';
1;

然后,您将确定要在外部使用哪个类(例如使用包装函数),而不是在任一类的构造函数中。


推荐阅读