什么是间接 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;
标题几乎概括了它,但这里还是长版。
在 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;