如何改进AUTOLOADed方法名称的语法?

时间:2014-11-21 18:55:06

标签: perl oop

如果我在这篇文章中滥用了某些术语,请原谅我。我正在推动我的Perl和编程技能超越我以前所做的,所以我不确定对于我想要做的一些事情的正确术语是什么。

我正在编写一个小型Perl模块,以便更轻松地与程序的XML-RPC API进行通信。我在我的模块类中创建了一些方法来处理最频繁的调用。然后,我正在使用AUTOLOAD为API的其余调用动态创建方法。这很有效,我对结果感到满意。

我希望提出的一个改进是改进我调用API方法的语法。 API将其调用分为命名空间,并使用句点作为命名空间分隔符。例如,一些调用是:

  • api.login
  • api.logout
  • user.create
  • user.delete

当我在Perl中调用它时,我无法在方法名称中使用句点,所以我用下划线替换它。这是我的AUTOLOAD方法。

sub AUTOLOAD {
    my $self = shift;
    our $AUTOLOAD;
    (my $method = $AUTOLOAD) =~ s/.*:://s; # remove package name
    $method =~ s/_/./g;
    return if $method =~ /DESTROY/;
    perform_rpc_call( $self, $method, @_ );
}

当我使用Perl代码进行调用时,它们看起来像这样:

$obj->user_delete( $username );

有没有比用下划线替换句号更优雅的方式?如果可能的话,我更愿意将方法调用为$obj->user->delete( $username )。是否有用于处理这种情况的模块或Perl设计模式?我花了两天的时间阅读Perl常见问题解答,搜索网络,并通过CPAN搜索无济于事。

我正在与之通信的API有一个调用,它将返回它支持的所有调用的列表。我想我可以在我的构造函数中检索它,并以某种方式使用它来使AUTOLOAD做我想要的。但我想知道是否有更好的方法。

1 个答案:

答案 0 :(得分:3)

如果您可以告诉user是名称空间或成员而不是通话,那么这是可能的。

package Class;

sub AUTOLOAD {
    (my $method = our $AUTOLOAD) =~ s/^.*:://s;
    return if $method =~ /DESTROY/;

    my $self = shift;

    return bless([$self, $method], 'Class::Helper');
        if $self->is_namespace($method);

    perform_rpc_call($self, $method, @_);
}


package Class::Helper;

sub AUTOLOAD {
    (my $method = our $AUTOLOAD) =~ s/^.*:://s;
    return if $method =~ /DESTROY/;

    my ($self, $ns) = @{ shift };
    $method = "$ns.$method";

    return bless([$self, $method], 'Class::Helper');
        if $self->is_namespace($method);

    perform_rpc_call($self, $method, @_);
}

这甚至适用于user.mail.send


如果您无法判断user是否是命名空间或来电,那么您所需的语法就会赢得' twork。您必须使用

$obj->user->delete->(@args)  # Error prone. See below

$obj->user->delete->call(@args)  # Same problem, but a little more obvious

$obj->api_call('user.delete', @args)

第一个选项非常容易出错。如果您忘记了最后->,则RPC方法不会被调用。我建议不要那个。

这是第二种选择的实施方式:

package Class;

sub AUTOLOAD {
    (my $method = our $AUTOLOAD) =~ s/^.*:://s;
    return if $method =~ /DESTROY/;

    my $self = shift;
    return bless([$self, $method], 'Class::Helper');
}


package Class::Helper;

sub AUTOLOAD {
    (my $method = our $AUTOLOAD) =~ s/^.*:://s;
    return if $method =~ /DESTROY/;

    my ($self, $ns) = @{ shift };
    $method = "$ns.$method";
    return bless([$self, $method], 'Class::Helper');
}

sub call {
    my ($self, $method) = @{ shift };
    perform_rpc_call($self, $method, @_);
}