Допустим, у вас есть родительский класс Perl в одном файле:
#!/usr/bin/perl
package Foo;
use strict;
use warnings;
use Data::Dumper;
sub new{
my $class = shift;
my %self = ();
return bless %self, $class;
}
1;
и подкласс в другом файле:
#!/usr/bin/perl
package Bar;
use base "Foo";
1;
Будет ли подкласс наследовать операторы использования от родителя? Я знаю, что новый метод будет наследоваться.
В основном я пытаюсь уменьшить количество стандартного кода в моем коде, и я не могу найти четкий ответ на этот вопрос.
Ах, хороший вопрос!
Will the subclass inherit the use statements from the parent?
Это зависит от того, что вы подразумеваете под наследованием. Я не буду делать никаких предположений до конца, но ответ будет , может быть, . Видите ли, perl смешивает идеи классов
и пространств имен
- пакет
- это термин, который может описывать любой из них. Теперь проблема заключается в заявлении use
, все, что он делает, - это принудительное включение пакета и вызов подпрограммы import ()
target. Это означает, что он, по сути, имеет неограниченный контроль над вашим пакетом - и, таким образом, над вашим классом.
Теперь добавьте сюда все методы в perl, представляющие собой не что иное, как subs
, которые принимают $ self
в качестве первого аргумента по соглашению, и вы останетесь с perl5. Это имеет огромные преимущества для тех, кто знает, как им пользоваться. Хотя strict - это лексическая прагма, как насчет Moose
?
package BigMooseUser;
use Moose;
package BabyMooseUser;
our @ISA = 'BigMooseUser';
package Foo;
my $b = BabyMooseUser->new;
print $b->meta->name;
Итак, откуда BabyMooseUser
получил конструктор (новый)? Откуда у него мета-класс? Все это обеспечивается одним использованием Moose;
в родительском классе (пространстве имен). Итак,
Will the subclass inherit the use statements from the parent?
Что ж, здесь, в нашем примере, если эффекты оператора use заключаются в добавлении методов, то, безусловно.
Это довольно глубокая тема, и это зависит от того, говорите ли вы о прагмах, или более неясных объектных фреймворках, или процедурных модулях. Если вы хотите смягчить влияние родительского пространства имен на ваше собственное в парадигме объектно-ориентированного программирования, см. namespace :: autoclean
.
Прагматический ответ на вашу проблему: либо используйте, либо посмотрите, как Modern :: Perl
делает это для обеспечения строгих требований и предупреждений.
Вы спросили в комментарии о Test::Most и о том, как он сокращает кодовую таблицу. Посмотрите на его метод import
. Он загружает модули в свое пространство имен, добавляет эти символы в @EXPORT
, затем повторно вызывает еще один import
через goto
, чтобы окончательно ввести их в вызывающее пространство имен. Это какая-то серьезная черная магия, которую применяет Кертис, хотя мне интересно, почему он просто не использовал что-то вроде import_to_level. Может быть, есть какие-то побочные эффекты, о которых я не думаю.
Я довольно много говорю о подобных вещах в Avoid accidently creating methods from module exports в The Effective Perler. Это в другом контексте, но это некоторые из тех же вопросов.
Вот другой пример.
Если какой-то другой модуль загружает модуль, у вас есть к нему доступ. Однако нехорошо зависеть от этого. Вот три отдельных файла:
Top.pm
use 5.010;
package Top;
use File::Spec;
sub announce { say "Hello from top!" }
1;
Bottom.pm
package Bottom;
use parent qw(Top);
sub catfiles { File::Spec->catfile( @_ ) }
1;
test.pl
use 5.010;
use Bottom;
say Bottom->catfiles( qw(foo bar baz) );
say File::Spec->catfile( qw( one two three ) );
Я загружаю File::Spec только в Top.pm. Однако после загрузки я могу использовать его в любом месте моей Perl-программы. Вывод показывает, что я смог "использовать" модуль в других файлах, хотя я загрузил его только в одном:
Bottom/foo/bar/baz
one/two/three
Чтобы это работало, часть кода, которая загружает модуль, должна загрузиться до того, как любая другая часть кода попытается использовать этот модуль. Как я уже сказал, полагаться на это - плохая идея: все сломается, если последовательность загрузки изменится или загружающий модуль исчезнет.
Однако если вы хотите импортировать символы, вы должны явно загрузить нужный вам модуль, находясь в пакете, в который вы хотите импортировать. Это нужно для того, чтобы экспортирующий модуль определил символы в этом пакете. Это не зависит от области видимости.
Для уменьшения количества шаблонов у меня есть несколько стратегий: Большинство моих классов - это классы Moose, который заботится о настройке OO, а также предоставляет мне строгие и предупреждающие условия. Если я хочу иметь функции, доступные во многих пакетах, я создаю модуль MyProject::Util
, который использует Sub-Exporter для предоставления мне моих собственных функций и моего собственного интерфейса. Это делает его более последовательным, и если я решу изменить Dumper (например) позже по какой-либо причине, мне не придется менять много кода. Это также позволит вам группировать экспорты. Класс обычно выглядит примерно так:
package Foo;
use Moose;
use MyProject::Util qw( :parsing :logging );
use namespace::autoclean;
# class implementation goes here
1;
Если есть другие вещи, которые вы считаете шаблонными и хотите упростить их включение, это, конечно, зависит от того, что это за вещи.
Вы можете получить окончательный ответ, изучив таблицы символов для каждого пакета:
# examine-symbol-tables.pl
use Bar;
%parent_names = map{$_ => 1} keys %Foo::;
%child_names = map{$_ => 1} keys %Bar::;
delete $parent_names{$_} && ($common_names{$_} = delete $child_names{$_}) foreach keys %child_names;
print "Common names in symbol tables:\n";
print "@{[keys %common_names]}\n\n";
print "Unique names in Bar symbol table:\n";
print "@{[keys %child_names]}\n\n";
print "Unique names in Foo symbol table:\n";
print "@{[keys %parent_names]}\n\n";
$ perl inherit.pl Common names in symbol tables: BEGIN Unique names in Bar symbol table: ISA isa import Unique names in Foo symbol table: Dumper new VERSION