RE: Когда использовать глобальную переменную?
Глобальные переменные являются рискованными, потому что они могут быть изменены в любое время любой частью кода, которая обращается к ним. Кроме того, трудно отследить, когда и где происходят изменения, что затрудняет отслеживание непреднамеренных последствий модификации. Короче говоря, каждая глобальная переменная увеличивает связь между подпрограммами, которые ее используют.
Когда имеет смысл использовать глобал? Когда выгоды перевешивают риски.
Если у вас много разных значений, необходимых большинству или всем вашим подпрограммам, похоже, пришло время использовать глобальные переменные. Вы можете упростить каждый вызов подпрограммы и сделать код более понятным, верно?
НЕПРАВИЛЬНО. В этом случае правильным подходом является объединение всех этих различных переменных в одну структуру данных контейнера. Таким образом, вместо foo( $frob, $grizzle, $cheese, $omg, $wtf );
у вас есть foo( $state, $frob );
Где $state = { grizzle => $grizzle, cheese => $cheese, omg => $omg, wtf => $wtf };
.
Так что теперь у нас есть одна переменная для передачи. Все эти суб-звонки намного проще. Тем не менее, несмотря на это, это обременительно, и вы все еще хотите убрать лишний аргумент из каждой процедуры.
На данный момент у вас есть несколько вариантов:
- Сделайте
$state
глобальным и просто получите к нему прямой доступ.
- Превратите
$state
в объект конфигурации и используйте методы для управления доступом к атрибутам.
- Превратить весь модуль в класс и сохранить всю информацию о состоянии в объекте.
Вариант 1 приемлем для небольших скриптов с несколькими подпрограммами. Риск трудных для отладки ошибок невелик.
Вариант 2 имеет смысл, когда нет очевидной связи между различными подпрограммами в модуле. Использование глобального объекта состояния помогает, потому что это уменьшает связь между кодом, который обращается к нему. Также проще добавить ведение журнала для отслеживания изменений в глобальных данных.
Вариант 3 работает хорошо, если у вас есть группа тесно связанных функций, которые работают с одними и теми же данными.
Ваш пример кода кажется хорошим кандидатом на вариант 3. Я создал класс с именем MySchema
, и все методы, работающие с конкретным каталогом, теперь являются методами. Вызывающий объект несет необходимые ему данные.
Теперь у нас есть хороший, чистый код и без глобалов.
use strict;
use warnings;
my @directories = (
'c:\\P4\\EDW\\PRODEDW\\EDWDM\\main\\db\\',
'c:\\P4\\EDW\\PRODEDW\\EDWADS\\main\\db\\',
'c:\\P4\\EDW\\PRODEDW\\FJE\\main\\db\\',
);
for my $schema ( make_schemata(@directories) ) {
$schema->run;
}
sub make_schemata {
my @schemata = map { MySchema->new( directory => $_ } @_;
return @schemata;
}
BEGIN {
package MySchema;
use Moose;
has 'directory' => (
is => 'ro',
isa => 'Str',
required => 1,
);
sub run {
my $self = shift;
$self->create_tables;
}
sub create_tables {
my $self = shift;
$self->sql_exec_folder('tbl');
}
sub sql_exec_folder {
my $self = shift;
my $dir = $self->directory;
print "In SQLExecFolder $dir\n";
}
1;
}
В качестве бонуса код в блоке BEGIN может быть удален и помещен в отдельный файл для повторного использования другим скриптом. Все, что нужно для полноценного модуля - это собственный файл с именем MySchema.pm
.