什么是间接对象表示法,为什么它不好,以及如何避免它?

Jar*_*und 8 oop perl coding-style perl5

标题几乎总结了,但无论如何这里是长版本.

在发布了一小段perl代码之后,我被告知要避免使用间接对象表示法,"因为它有几个副作用".评论引用了这一特定的行:

my $some_object = new Some::Module(FIELD => 'value');
Run Code Online (Sandbox Code Playgroud)

因为这就是我一直以来的做法,为了与时俱进,我因此问:

  • 这有什么不好的?(特别)
  • 潜在的(可能是负面的)副作用是什么?
  • 应该如何重写该行?

我正要问这位评论者,但对我而言,这是值得发表的.

cjm*_*cjm 18

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

my $some_object = new Some::Module(FIELD => 'value');
Run Code Online (Sandbox Code Playgroud)

意味着调用包中的new方法Some::Module,还是意味着调用new当前包中的Module函数,结果是Some使用给定的参数调用包中的函数?

即,它可以被解析为:

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

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

通常,解析器会正确猜测,但最佳做法是避免歧义.


ike*_*ami 9

这有什么不好的?

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

主要问题是很容易意外地调用错误的功能.请使用以下代码,例如:

package Widget;

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

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

1;
Run Code Online (Sandbox Code Playgroud)

在该代码中,new SubWidget预计意味着

SubWidget->new()
Run Code Online (Sandbox Code Playgroud)

相反,它实际上意味着

new("SubWidget")
Run Code Online (Sandbox Code Playgroud)

也就是说,使用strict将捕获大多数此错误的实例.如果use strict;要添加到上面的代码段,将产生以下错误:

Bareword "SubWidget" not allowed while "strict subs" in use at Widget.pm line 11.
Run Code Online (Sandbox Code Playgroud)

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

这意味着

  • 使用没有parens的间接对象表示法可能会导致奇怪的错误消息.
  • 使用带有parens的间接对象表示法可能会导致调用错误的代码.

前者是可以忍受的,而后者是可以避免的.但是,我们只是告诉人们"避免使用间接方法表示法",而不是告诉人们"避免在使用间接方法表示法的方法调用的参数周围使用parens".它太脆弱了.


还有另一个问题.这不仅仅是使用间接对象表示法这是一个问题,它在Perl中支持它.该特征的存在导致多个问题.首先,

  • 它会导致一些语法错误导致非常奇怪/误导性的错误消息,因为代码似乎是在没有时使用ION.
  • 它阻止实现有用的功能,因为它们与有效的ION语法冲突.

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


应该如何重写该行?

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

my $some_object = Some::Module->new(FIELD => 'value');
Run Code Online (Sandbox Code Playgroud)

也就是说,即使这种语法也不明确.它将首先检查名为的函数是否Some::Module存在.但这种情况非常不可能,很少有人能够保护自己免受此类问题的影响.如果您想保护自己,可以使用以下内容:

my $some_object = Some::Module::->new(FIELD => 'value');
Run Code Online (Sandbox Code Playgroud)


Leo*_*erd 6

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

no indirect;
Run Code Online (Sandbox Code Playgroud)

http://metacpan.org/pod/indirect