пакет My :: Module
Ваши модули должны быть независимы от контекста. То есть они не должны ожидать, что $dbh
является обработчиком базы данных или что они должны возвращать вещи в $q
. или эта конфигурация сохраняется в %conf
.
Например, что если вы вдруг окажетесь с двумя экземплярами дескрипторов базы данных? Чем ты занимаешься? Я ненавижу, когда модуль требует, чтобы я использовал специфичные для модуля переменные для конфигурации, потому что это означает, что я не могу использовать два разных экземпляра этого модуля.
Итак, у вас есть два варианта:
- Либо передайте необходимые данные каждый раз.
- Создайте экземпляр объекта (это правильно, объектно-ориентированное программирование) для хранения необходимой информации.
Давайте рассмотрим первый экземпляр, используя ваш псевдокод:
sub someFunction (
%param = @_;
%conf = %{param{conf};
@data = $param{dbh}->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
)
1;
Пример псевдо-скрипта:
use CGI;
use DBI;
use My::Module;
my $q = new CGI;
my $dbh = some_connenction_function($dsn);
my %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );
someFunction (
q => $q,
dbh => $dbh,
conf => \%conf,
);
Здесь я использую вызовы параметров. Не так уж плохо. Это? Теперь, если вам нужен другой оператор select, вы можете использовать разные переменные.
Конечно, но что, если вы не хотите постоянно передавать переменные. Итак, вы можете использовать Объектно-ориентированные методы . Теперь расслабься и успокойся. Есть много, много веских причин для использования объектно-ориентированного дизайна:
- Это может упростить ваше программирование : Это сбивает с толку людей, потому что объектно-ориентированное программирование означает думать о том, как ваша программа будет работать, затем разрабатывать ее, а затем создавать различные объекты. Все, что вы хотите сделать, это код и убрать его с дороги. Дело в том, что если вы думаете о своей программе и разрабатываете ее, она работает лучше, а код - быстрее. Аспект дизайна исключает сложность вашего основного кода и надежно убирает его в небольшие, легко усваиваемые процедуры.
- Это то, что круто в Perl : Вам придется привыкнуть к объектно-ориентированному Perl, потому что это то, что пишут все остальные. Вы увидите множество операторов типа
my $foo = Foo::Bar->new;
. Более новые модули Perl имеют только объектно-ориентированные интерфейсы, и вы не сможете их использовать.
- Птенцы копают это : Эй, я просто хватаюсь за соломинку здесь ...
Давайте посмотрим, как может работать объектно-ориентированный подход. Для начала посмотрим на основную программу:
use CGI;
use DBI;
use My::Module;
my $q = new CGI;
my $dbh = some_connenction_function($dsn);
my %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );
my $mod_handle = My::Module->new (
q => $q,
dbh => $dbh,
conf => \%conf,
);
$mod_handle->someFunction;
Выше я создаю object instance
, который содержит эти переменные. И, волшебным образом, я изменил ваши Функции на Методы . Метод - это просто функция в вашем Class (он же модуль). Хитрость в том, что мой экземпляр (переменная $mod_handler
содержит все необходимые переменные, хранящиеся в хорошем и удобном для вас виде. Синтаксис $mod_hander->
просто передает эту информацию для моих функций Я имею в виду методы.
Итак, как теперь выглядит ваш модуль? Давайте посмотрим на первую часть, где у меня есть Constructor , который является просто функцией, которая создает хранилище для моих переменных, которые мне нужны:
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->{Q} = $q;
$self->{DBH} = $dbh;
$self->{CONF} = $conf;
return $self;
}
Давайте рассмотрим первое, что немного отличается: my $class = shift;
. Откуда это? Когда я вызываю функцию с синтаксисом Foo->Bar
, я передаю Foo
в качестве первого параметра в функции Bar
. Таким образом, $class
равно My::Module
. Это так же, как если бы я назвал вашу функцию следующим образом:
my $mod_handle = My::Module::new("My::Module", %params);
вместо:
my $mod_handle = My::Module->new(%params);
Следующая вещь - это строка my $self = {};
. Это создает ссылку на хеш. Если вы не понимаете ссылки, вам следует обратиться к Справочному руководству Марка , которое включено в Perldocs. По сути, ссылка - это область памяти, где хранятся данные. В этом случае у моего хэша нет имени, все, что у меня есть, это ссылка на память, в которой он хранится, и называется $self
. В Perl нет ничего особенного в названии new
или $self
, но это стандарты, которым все в значительной степени следуют.
Команда bless
берет мою ссылку $self
и объявляет ее типом My::Module
. Таким образом, Perl может отслеживать, является ли $mod_handle
типом экземпляра , который имеет доступ к этим функциям.
Как видите, ссылка $self
содержит все переменные, которые нужны моим функциям. И я удобно передаю это обратно в мою основную программу, где храню ее в $mod_handle
.
Теперь давайте посмотрим на мои методы :
sub SomeFunction {
$self = shift;
my $dbh = $self->{DBH};
my $q = $self->{Q};
my %conf = %{self->{CONF}};
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
Опять эта строка $self = shift;
. Помните, я называю это как:
$mod_handle->SomeFunction;
То же самое, что называть это:
My::Module::SomeFunction($mod_handle);
Таким образом, значение $self
является ссылкой на хэш, которую я хранил в $mod_handle
. И эта ссылка на хеш содержит три значения, которые я всегда передаю этой функции.
Заключение
Вы никогда не должны делить переменные между вашей основной программой и вашим модулем. В противном случае, вы застреваете не только с одним и тем же именем в вашей программе каждый раз, но вы должны быть осторожны, чтобы не использовать ваш модуль параллельно в другой части вашей программы.
Используя объектно-ориентированный код, вы можете хранить нужные переменные в одном экземпляре и передавать их назад и вперед между функциями в одном экземпляре. Теперь вы можете вызывать переменные в своей программе как хотите, и можете использовать свой модуль в параллельных экземплярах. Это улучшает вашу программу и ваши навыки программирования.
Кроме того, вы также можете привыкнуть к объектно-ориентированному программированию, потому что оно не исчезнет. Это работает слишком хорошо. Целые языки предназначены исключительно для объектно-ориентированного подхода, и если вы не понимаете, как это работает, вы никогда не улучшите свои навыки.
И я упоминал, что цыплята копали это?
Adium
Прежде чем все хакеры Perl обрушатся на меня. Я хочу отметить, что мой объектно-ориентированный дизайн Perl очень плохой . Это намного лучше, чем вы хотели, но в этом есть серьезный недостаток: я раскрыл дизайн моего объекта для всех методов в моем классе. Это означает, что если я изменю способ хранения своих данных, мне придется пройти весь мой модуль, чтобы найти и заменить его.
Я сделал это таким образом, чтобы сделать его простым и сделать немного более очевидным, чем я занимался. Однако, как скажет вам любой хороший объектно-ориентированный программист (и второстепенные хаки, как я), вы должны использовать функции setter / getter для установки значений вашего члена.
Функция установки довольно проста. Шаблон выглядит так:
sub My_Method {
my $self = shift;
my $value = shift;
# Something here to verify $value is valid
if (defined $value) {
$self->{VALUE} = $value;
}
return $self->{VALUE};
}
Если я вызову $instance->My_Method("This is my value");
в моей программе, для $self->{VALUE}
будет установлено значение This is my value
. В то же время он возвращает значение $self->{VALUE}
.
Теперь, допустим, я все это так:
my $value = $instance->My_Method;
Мой параметр $value
равен undefined , поэтому я не устанавливаю значение $self->{VALUE}
. Тем не менее, я все равно возвращаю значение.
Таким образом, я могу использовать тот же метод для установки и получения моего значения.
Давайте посмотрим на мой Конструктор (это причудливое название для этой new
функции):
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->{Q} = $q;
$self->{DBH} = $dbh;
$self->{CONF} = $conf;
return $self;
}
Вместо установки ссылки на хеш $self->{}
непосредственно в этой программе, хороший дизайн сказал, что я должен был использовать функции получения / установки как это:
sub new {
my $class = shift;
my %param = @_;
my $self = {};
bless $self, $class
$self->Q = $q; #This line changed
$self->Dbh = $dbh; #This line changed
$self->Conf = $conf; #This line changed
return $self;
}
Теперь мне нужно определить эти три подпрограммы, Q
, Dbh
и Conf
, но теперь мой метод SomeFunction исходит из этого:
sub SomeFunction {
$self = shift;
my $dbh = $self->{DBH};
my $q = $self->{Q};
my %conf = %{self->{CONF}};
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
На это:
sub SomeFunction {
$self = shift;
my $dbh = $self->Dbh; #This line changed
my $q = $self->Q; #This line changed
my %conf = %{self->Conf}; #This line changed
@data = $dbh->selectrow_array(
"SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar}
);
return $param{q}->p( "@data" );
}
Изменения неуловимы, но важны. Теперь моя new
функция и моя SomeFunction
понятия не имеют, как эти параметры хранятся. Единственное место, которое знает, как они хранятся, - это сама функция получения / установки. Если я изменяю структуру данных класса, мне не нужно ничего изменять, кроме самих функций получения / установки.
Постскриптум
Вот пища для размышлений ...
Если все ваши вызовы SQL находятся в вашей функции My :: Module, почему бы просто не инициализировать $dbh
и $q
там в первую очередь. Таким образом, вам не нужно включать модуль Use Dbi;
в вашу программу. Фактически, ваша программа теперь остается в блаженном неведении, как именно хранятся данные. Вы не знаете, является ли это базой данных SQL, базой данных Mongo или даже какой-то плоской структурой ББли в стиле Беркли.
Я включаю модуль Я давно выполнил работу, где пытался упростить использование нашей базы данных. Этот модуль сделал несколько вещей:
- Все обработано в базе данных. Инициализация и все ручки. Таким образом, вашей основной программе не нужно было использовать какой-либо модуль, кроме этого.
- Он также отошел от разработчика, пишущего операторы select. Вместо этого вы определили, какие поля вам нужны, и выяснили, как сделать запрос за вас.
- Возвращает инкриминаторную функцию , которая используется для извлечения следующей строки из базы данных.
Посмотрите на это. Это не самое лучшее, но вы увидите, как я использую объектно-ориентированный дизайн для устранения всех проблем, которые у вас возникают. Чтобы просмотреть документацию, просто введите perldoc MFX:Cmdata
в командной строке.