如何编写一个既可用作普通子程序又可用作类方法的Perl子程序?

时间:2009-06-24 15:09:19

标签: perl

我正在写一个大多数是静态的函数。我想将它插入Template Toolkit,它传递类名。实质上,它正在做

ClassName->function( $args.. )

但我希望它能像

那样做
ClassName::function( $args.. )

里面

sub function {
}

处理这两种情况的正确方法是什么?

3 个答案:

答案 0 :(得分:7)

一般来说,没有。 a sub要么被编写为方法,要么不是。

通过将包名称添加到参数列表中,了解File::Spec::Functions如何处理这种情况。

现在, 在一个非常具体的,有限的案例中 ,您可以这样做:

shift if $_[0] eq __PACKAGE__;

作为sub中的第一行,当sub被称为类方法时,丢弃第一个参数。

答案 1 :(得分:6)

这是一个更安全的版本,结合了Sinan'sAlan's个答案,还有:

  • 处理从派生对象调用方法的可能性
  • 不会将ClassName::function("ClassName")误解为方法调用

代码:

if (@_ == $nArgsExpectedForThisFunc + 1) {
    $_[0] eq __PACKAGE__ || UNIVERSAL::isa($_[0], __PACKAGE__) || die;
    shift;
}

这要求您知道预期的参数数量;如果不这样做,但是您的第一个参数可以与作为方法调用时可能发送的可能允许值区分开(例如,如果您的第一个参数必须是arrayref),则仍然可以应用相同的一般原则。

答案 2 :(得分:4)

模板工具包期望它的插件使用OO,因此无法提供该接口。如果你还想要一个功能界面,你有几个选择。

Perl并没有真正区分函数和方法。主要区别在于方法调用语法隐式地将对象引用(或类名,取决于它的调用方式)作为第一个参数。您可以使用函数调用语法并手动提供指示对象:

ClassName::function('ClassName', @args);

但那太乱了。更清洁的解决方案是将其拆分为两个子,其中一个包装为另一个。 e.g。

package ClassName;

sub function {
    # do something
}

sub method {
    my $class = shift;
    function(@_);
}

该函数也可以是该方法的包装器。正如Sinan所提​​到的,File :: Spec通过创建两个模块来实现这一点:一个带有OO接口,另一个带有功能接口。