什么是间接 object 表示法,为什么不好,如何避免?

What is indirect object notation, why is it bad, and how does one avoid it?

标题几乎概括了它,但这里还是长版。

在 post 编写一小段 perl 代码后,我被告知要避免间接 object 表示法,"as it has several side effects"。评论引用了这一行:

my $some_object = new Some::Module(FIELD => 'value');

因为我一直都是这样做的,为了与时俱进,所以我问:

我正要问评论者,但对我来说这是值得的post。

评论者只是想将 Some::Module->new(FIELD => 'value'); 视为构造函数。

Perl 可以使用 indirect object syntax for other bare words that look like they might be methods,但现在 perlobj 文档建议不要使用它。

它的一般问题是,以这种方式编写的代码是模棱两可的,并且会使用 Perl 的解析器来测试命名空间,例如当你写 method Namespace 时检查 Namespace::method 是否存在。

主要问题是它含糊不清。是否

my $some_object = new Some::Module(FIELD => 'value');

意思是调用Some::Module包中的new方法,还是调用当前包中的new函数,结果调用[=15] =] Some 包中具有给定参数的函数?

即可以解析为:

# method call
my $some_object = Some::Module->new(FIELD => 'value');
# or function call
my $some_object = new(Some::Module(FIELD => 'value'));

另一种方法是使用显式方法调用符号 Some::Module->new(...)

通常情况下,解析器会正确猜测,但最好的做法是避免歧义。

What's so bad about it?

间接方法表示法的问题是可以避免的,但告诉人们避免使用间接方法表示法要容易得多。

主要问题很容易不小心调用错误的函数。以下面的代码为例:

package Widget;

sub new { ... }
sub foo { ... }
sub bar { ... }

sub method {
   ...;
   my $o = new SubWidget;
   ...;
}

1;

在该代码中,new SubWidget 应表示

SubWidget->new()

相反,它实际上意味着

new("SubWidget")

也就是说,使用 strict 将捕获此错误的大部分实例。如果将 use strict; 添加到上面的代码片段中,将产生以下错误:

Bareword "SubWidget" not allowed while "strict subs" in use at Widget.pm line 11.

也就是说,有些情况下使用 strict 不会捕获错误。它们主要涉及在方法调用的参数周围使用括号(例如 new SubWidget($x))。

也就是说

  • 使用不带括号的间接宾语表示法会导致出现奇怪的错误消息。
  • 使用带括号的间接对象表示法会导致调用错误的代码。

前者是可以忍受的,后者是可以避免的。但与其告诉人们 "avoid using parens around the arguments of method calls using Indirect Method Notation",我们只是告诉人们 "avoid using Indirect Method Notation"。就是太脆弱了。


还有一个问题。问题不只是使用间接对象表示法,它在 Perl 中支持它。该功能的存在会导致多个问题。首先,

  • 它会导致一些语法错误,导致非常 odd/misleading 的错误消息,因为代码似乎在使用 ION,但实际上并没有。
  • 它阻止了有用的功能的实现,因为它们与有效的 ION 语法冲突。

从好的方面来说,使用 no indirect; 有助于解决第一个问题。


How should that line be rewritten?

方法调用的正确写法如下:

my $some_object = Some::Module->new(FIELD => 'value');

也就是说,即使是这种语法也是模棱两可的。它将首先检查名为 Some::Module 的函数是否存在。但这不太可能,以至于很少有人保护自己免受此类问题的影响。如果您想保护自己,可以使用以下方法:

my $some_object = Some::Module::->new(FIELD => 'value');

至于如何避免它:有一个禁止表示法的 CPAN 模块,就像 pragma 模块一样:

no indirect;

http://metacpan.org/pod/indirect