use*_*829 8 oop perl constructor method-invocation indirect-objects
我是Perl的新手,目前正在学习Perl面向对象,并且遇到了编写构造函数.看起来当使用new
子例程的名称时,第一个参数将是包名.
构造函数必须使用关键字new
吗?或者是因为当我们new
使用packagename 调用子例程时,要传入的第一个参数是包名吗?
packagename->new;
Run Code Online (Sandbox Code Playgroud)
当子程序有其他名称时,第一个参数将是对象的引用?或者是因为当通过对对象的引用调用子例程时,要传入的第一个参数将是对象的引用?
$objRef->subroutine;
Run Code Online (Sandbox Code Playgroud)
tch*_*ist 11
注意:以下所有示例都是为了教学目的而简化的.
是的,你是对的.new
函数的第一个参数,如果作为方法调用,将是你调用它的东西.
调用方法有两种"风格",但结果都是相同的.一种风味依赖于操作员,即二元->
运算符.另一种味道依赖于参数的排序,比特动词在英语中的运作方式.大多数人只使用内置函数的dative/bitransitive样式 - 也许使用构造函数,但很少使用其他任何东西.
在大多数(但不是全部)情况下,前两个是等价的:
1. 方法的本能调用
这是位置的,使用单词顺序来确定发生了什么.
use Some::Package;
my $obj1 = new Some::Package NAME => "fred";
Run Code Online (Sandbox Code Playgroud)
请注意,我们在那里没有使用方法箭头:没有->
写入.这就是Perl本身使用的许多功能,例如
printf STDERR "%-20s: %5d\n", $name, $number;
Run Code Online (Sandbox Code Playgroud)
几乎每个人都喜欢相同的东西:
STDERR->printf("%-20s: %5d\n", $name, $number);
Run Code Online (Sandbox Code Playgroud)
然而,现在这种类型的dative调用几乎只用于内置函数,因为人们总是把事情搞得一团糟.
2. 箭头调用方法
箭头调用在很大程度上更清晰,更清晰,并且不太可能让你纠结在Perl解析奇怪的杂草中.注意我说的可能性较小 ; 我并没有说这是免费的所有infelicities的.但是,为了这个答案的目的,我们只是假装.
use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");
Run Code Online (Sandbox Code Playgroud)
在运行时,除了任何奇特的奇怪或继承问题,实际的函数调用将是
Some::Package::new("Some::Package", "NAME", "fred");
Run Code Online (Sandbox Code Playgroud)
例如,如果您在Perl调试器中并进行了堆栈转储,那么它将具有类似于其调用链中的上一行的内容.
由于调用方法始终使用invocant作为参数列表的前缀,因此将作为方法调用的所有函数都必须考虑该"额外"的第一个参数.这很容易做到:
package Some::Package;
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
Run Code Online (Sandbox Code Playgroud)
这只是调用构造函数的最常见方法的极其简化的示例,以及内部发生的情况.在实际的生产代码中,构造函数会更加谨慎.
有时您在编译时不知道类名或方法名,因此您需要使用变量来保存其中一个或两个,或两者.编程中的间接与自然语言中的间接对象不同.间接只是意味着你有一个包含其他东西的变量,所以你使用变量来获取它的内容.
print 3.14; # print a number directly
$var = 3.14; # or indirectly
print $var;
Run Code Online (Sandbox Code Playgroud)
我们可以使用变量来保存方法调用中涉及的其他内容,而不仅仅是方法的参数.
3.具有间接方法名称的箭头调用:
如果您不知道方法名称,则可以将其名称放在变量中.只能使用箭头调用来尝试此操作,而不是使用dative调用.
use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj = Some::Package->$action(NAME => "fido");
Run Code Online (Sandbox Code Playgroud)
这里方法名称本身在运行时才知道.
4.具有间接类名的箭头调用:
这里我们使用一个变量来包含我们想要使用的类的名称.
my $class = (rand(2) < 1)
? "Fancy::Class"
: "Simple::Class";
my $obj3 = $class->new(NAME => "fred");
Run Code Online (Sandbox Code Playgroud)
现在我们随机选择一个或另一个类.
你也可以用这种方式实现dative调用:
my $obj3 = new $class NAME => "fred";
Run Code Online (Sandbox Code Playgroud)
但通常不会使用用户方法.但有时会发生内置插件.
my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;
Run Code Online (Sandbox Code Playgroud)
那是因为尝试在dative槽中使用表达式通常不会在没有块的情况下工作; 否则它只能是一个简单的标量变量,甚至不是数组或散列中的单个元素.
printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;
Run Code Online (Sandbox Code Playgroud)
或者更简单:
print { $fh{$filename} } "Some data.\n";
Run Code Online (Sandbox Code Playgroud)
这是非常难看的丑陋.
请注意,这不是完美的.dative对象槽中的文字与变量的工作方式不同.例如,使用文字文件句柄:
print STDERR;
Run Code Online (Sandbox Code Playgroud)
手段
print STDERR $_;
Run Code Online (Sandbox Code Playgroud)
但是如果你使用间接文件句柄,像这样:
print $fh;
Run Code Online (Sandbox Code Playgroud)
这实际上意味着
print STDOUT $fh;
Run Code Online (Sandbox Code Playgroud)
这不太可能意味着你想要什么,这可能是这样的:
print $fh $_;
Run Code Online (Sandbox Code Playgroud)
又名
$fh->print($_);
Run Code Online (Sandbox Code Playgroud)
关于方法调用箭头的事情->
是,它的左手操作数是表示类名的字符串还是表示对象实例的祝福引用是不可知的.
当然,没有正式要求$class
包含包名称.它可能是,或者如果是这样,由方法本身来做正确的事情.
use Some::Class;
my $class = "Some::Class";
my $obj = $class->new(NAME => "Orlando");
my $invocant = (rand(2) < 1) ? $class : $obj;
$invocant->any_debug(1);
Run Code Online (Sandbox Code Playgroud)
这需要一个非常奇特的any_debug
方法,根据它的调用者是否有福来做一些不同的方法:
package Some::Class;
use Scalar::Util qw(blessed);
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
sub any_debug {
my($invocant, $value) = @_;
if (blessed($invocant)) {
$invocant->obj_debug($value);
} else {
$invocant->class_debug($value);
}
}
sub obj_debug {
my($self, $value) = @_;
$self->{DEBUG} = $value;
}
my $Global_Debug;
sub class_debug {
my($classname, $value) = @_;
$Global_Debug = $value;
}
Run Code Online (Sandbox Code Playgroud)
然而,这是一种相当先进和微妙的技术,仅适用于少数几种不常见的情况.在大多数情况下不建议这样做,因为如果处理不当可能会造成混淆 - 甚至可能是这样.
它不是第一个参数new
,而是间接对象语法,
perl -MO=Deparse -e 'my $o = new X 1, 2'
Run Code Online (Sandbox Code Playgroud)
被解析为
my $o = 'X'->new(1, 2);
Run Code Online (Sandbox Code Playgroud)
从perldoc,
Perl 支持另一种称为“间接对象”表示法的方法调用语法。这种语法称为“间接”,因为该方法出现在调用它的对象之前。
也就是说,new
不是某种构造函数调用的保留字,而是方法/构造函数本身的名称,在 perl 中没有强制执行(即DBI
具有connect
构造函数)