Примечание. Все приведенные ниже примеры упрощены в учебных целях.
О методах
Да вы правы. Первым аргументом вашей функции new
, если вызывается как метод, будет то, против чего вы ее вызвали.
Есть два «разновидности» вызова метода, но результат один и тот же в любом случае. Один вариант зависит от оператора, бинарного оператора ->
. Другой вариант основан на упорядочении аргументов, подобно тому, как бипереходные глаголы работают в английском языке. Большинство людей используют дательный/двупереходный стиль только со встроенными и возможно с конструкторами, но редко с чем-либо еще.
В большинстве (но не во всех) обстоятельствах эти первые два эквивалентны:
<сильный>1. Дательный падеж Вызов методов
Это позиционный, тот, который использует порядок слов, чтобы определить, что происходит.
use Some::Package;
my $obj1 = new Some::Package NAME => "fred";
Обратите внимание, что мы не используем здесь стрелку метода: нет ->
, как написано. Это то, что сам Perl использует со многими своими собственными функциями, такими как
printf STDERR "%-20s: %5d\n", $name, $number;
Что почти все предпочитают эквиваленту:
STDERR->printf("%-20s: %5d\n", $name, $number);
Однако в наши дни такого рода обращение к дательному падежу используется почти исключительно для встроенных функций, потому что люди продолжают путать вещи.
<сильный>2. Стрелка Вызов методов
Вызов стрелки по большей части более четкий и чистый, и с меньшей вероятностью запутает вас в сорняках Perl-анализа странностей. Обратите внимание, я сказал менее вероятно; Я не говорил, что он свободен от всех недостатков. Но давайте просто притворимся, что это так для целей этого ответа.
use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");
Во время выполнения, за исключением каких-либо причудливых странностей или вопросов наследования, фактический вызов функции будет
Some::Package::new("Some::Package", "NAME", "fred");
Например, если бы вы работали в отладчике Perl и сделали дамп стека, в цепочке вызовов было бы что-то похожее на предыдущую строку.
Поскольку вызов метода всегда добавляет префикс invocant к списку параметров, все функции, которые будут вызываться как методы, должны учитывать этот «дополнительный» первый аргумент. Это делается очень легко:
package Some::Package;
sub new {
my($classname, @arguments) = @_;
my $obj = { @arguments };
bless $obj, $classname;
return $obj;
}
Это просто чрезвычайно упрощенный пример новых наиболее частых способов вызова конструкторов и того, что происходит внутри. В реальном производственном коде конструктор был бы намного осторожнее.
Методы и косвенность
Иногда вы не знаете имя класса или имя метода во время компиляции, поэтому вам нужно использовать переменную для хранения того или другого, или обоих. Косвенность в программировании отличается от косвенных объектов в естественном языке. Косвенность просто означает, что у вас есть переменная, которая содержит что-то еще, поэтому вы используете переменную, чтобы получить ее содержимое.
print 3.14; # print a number directly
$var = 3.14; # or indirectly
print $var;
Мы можем использовать переменные для хранения других вещей, связанных с вызовом метода, помимо аргументов метода.
<сильный>3. Вызов стрелки с косвенным именем метода:
Если вы не знаете имя метода, вы можете поместить его имя в переменную. Попробуйте это только с вызовом стрелки, а не с вызовом дательного падежа.
use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj = Some::Package->$action(NAME => "fido");
Здесь имя самого метода неизвестно до времени выполнения.
<сильный>4. Вызов стрелки с косвенным именем класса:
Здесь мы используем переменную для хранения имени класса, который мы хотим использовать.
my $class = (rand(2) < 1)
? "Fancy::Class"
: "Simple::Class";
my $obj3 = $class->new(NAME => "fred");
Теперь мы случайным образом выбираем тот или иной класс.
Вы также можете использовать дательный падеж таким образом:
my $obj3 = new $class NAME => "fred";
Но это обычно не делается с помощью пользовательских методов. Однако иногда это происходит со встроенными.
my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;
Это потому, что попытка использовать выражение в слоте дательного падежа вообще не будет работать без блока вокруг него; в противном случае это может быть только простая скалярная переменная, даже не один элемент из массива или хеша.
printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;
Или проще:
print { $fh{$filename} } "Some data.\n";
Что чертовски некрасиво.
Пусть инициатор остерегается
Обратите внимание, что это не работает идеально. Литерал в слоте дательного падежа работает иначе, чем переменная. Например, с буквальными файловыми дескрипторами:
print STDERR;
означает
print STDERR $_;
но если вы используете косвенные файловые дескрипторы, например:
print $fh;
Это на самом деле означает
print STDOUT $fh;
что вряд ли означает то, что вы хотели, что, вероятно, было так:
print $fh $_;
он же
$fh->print($_);
Продвинутое использование: Методы двойной природы
Особенность стрелки вызова метода ->
заключается в том, что она не зависит от того, является ли ее левый операнд строкой, представляющей имя класса, или благословенной ссылкой, представляющей экземпляр объекта.
Конечно, ничто формально не требует, чтобы $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);
Для этого требуется довольно причудливый метод 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;
}
Однако это довольно продвинутая и тонкая техника, применимая лишь в нескольких необычных ситуациях. Это не рекомендуется для большинства ситуаций, так как может привести к путанице при неправильном обращении — и, возможно, даже если это так.
person
tchrist
schedule
08.06.2014
new
. Это обычное имя для конструктора объекта, и оно используется многими классами, но ни в коем случае не является обязательным. - person tchrist   schedule 08.06.2014