如何在Perl OO模块中实现调度表?

iai*_*ain 10 oop perl

我想将一些OO包中的subs放入一个数组中 - 也在包中 - 用作调度表.像这样的东西

package Blah::Blah;

use fields 'tests';

sub new {
    my($class )= @_;

    my $self = fields::new($class);

    $self->{'tests'} = [
                         $self->_sub1
                        ,$self->_sub2
                       ];
    return $self;
}

_sub1 { ... };
_sub2 { ... };
Run Code Online (Sandbox Code Playgroud)

我不完全确定这个的语法?

$self->{'tests'} = [
                         $self->_sub1
                        ,$self->_sub2
                       ];
Run Code Online (Sandbox Code Playgroud)

要么

$self->{'tests'} = [
                         \&{$self->_sub1}
                        ,\&{$self->_sub2}
                       ];
Run Code Online (Sandbox Code Playgroud)

要么

$self->{'tests'} = [
                         \&{_sub1}
                        ,\&{_sub2}
                       ];
Run Code Online (Sandbox Code Playgroud)

我似乎无法在OO包中使用它,而在程序方面它非常简单,我没有找到OO的任何示例.

Iain非常感谢任何帮助

Rob*_*t P 11

你的朋友是can.它返回对子例程的引用(如果存在),否则返回null.它甚至可以正确地走向OO链.

$self->{tests} = [
    $self->can('_sub1'),
    $self->can('_sub2'),
];

# later

for $tn (0..$#{$self->{tests}}) {
    ok defined $self->{tests}[$tn], "Function $tn is available.";
}

# and later

my $ref = $self->{tests}[0];
$self->$ref(@args1);
$ref = $self->{tests}[1];
$self->$ref(@args2);
Run Code Online (Sandbox Code Playgroud)

或者,由于这个问题(恰好是这个问题的变体),你可以直接调用它:

$self->${\$self->{tests}[0]}(@args1);
$self->${\$self->{tests}[1]}(@args1);
Run Code Online (Sandbox Code Playgroud)

请注意,它\为我们提供了对subref的引用,然后由${}after 解除引用$self->.呼!

为了解决大脑提到的及时性问题,另一种方法是简单地让{test}成为一个子程序,返回一个ref,然后你就可以在你需要的时候得到它:

sub tests {
    return [ 
        $self->can('_sub1'),
        $self->can('_sub2')
    ];
}
Run Code Online (Sandbox Code Playgroud)

然后使用它:

for $tn (0..$#{$self->tests()}) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

当然,如果你不得不迭代refs,你也可以直接将参考传递出来:

for my $ref (0..$#{$self->tests()}) {
    $self->$ref(@args);
}
Run Code Online (Sandbox Code Playgroud)


bri*_*foy 6

虽然罗伯特P的答案可能对你有用,但它有一个问题就是在这个过程中尽早修复调度.我倾向于尽可能晚地解决这些方法,所以我会把tests数组中的东西留作方法名称,直到你想要使用它们:

 $self->{tests} = [
     qw( _sub1 _sub2 )
     ];
Run Code Online (Sandbox Code Playgroud)

动态语言的优势在于,只要您愿意决定将要发生什么,您就可以等待.

当你想运行它们时,你可以经历与罗伯特已经注意到的相同的过程.我会为它添加一个接口:

  foreach my $method_name ( $obj->get_test_methods )
      {
      $obj->$method_name();
      }
Run Code Online (Sandbox Code Playgroud)

这可能会更好,因为没有将测试绑定到现有的方法名称:

  foreach my $method_name ( $obj->get_test_methods )
      {
      $obj->run_test_named( $method_name );
      }
Run Code Online (Sandbox Code Playgroud)

run_test_named可能是你的调度员,它可以非常灵活:

 sub run_test_named
      {
      my( $self, $name ) = @_;

      # do anything you want, like in Robert's answer
      }
Run Code Online (Sandbox Code Playgroud)

您可能想要做的一些事情:

  • 在对象上运行方法
  • 将对象作为参数传递给其他东西
  • 暂时覆盖测试
  • 没做什么
  • 等等

当您将自己决定要执行的内容与其实现分开时,您可以获得更多自由.不仅如此,下次调用相同的测试名称时,您可以执行不同的操作.


cjm*_*cjm 6

use lib Alpha;

my $foo = Alpha::Foo->new; # indirect object syntax is deprecated

$foo->bar();

my %disp_table = ( bar => sub { $foo->bar() } );

$disp_table{bar}->(); # call it
Run Code Online (Sandbox Code Playgroud)

您需要一个闭包,因为您想将方法调用转换为普通的子例程调用,因此您必须捕获您正在调用该方法的对象.

  • 你不一定需要关闭.你可以有一个方法名称的调度表,并执行`my $ meth = $ table {bar}; $ foo - > $ meth;`或者你可能很傻并且做`$ foo - > $ {\ $ table {bar}} (4认同)