Как использовать основные :: данные в модулях?

В скрипте я инициализируется несколько обработчиков и задаются переменные, которые должны быть доступны для функций в отдельных модулях. Каков наилучший способ их использования ($q, $dbh, %conf) в модулях?

Пример псевдомодуля:

package My::Module

sub SomeFunction (
 @data = $dbh->selectrow_array("SELECT * FROM Foo WHERE Bar = ?", undef, $conf{Bar} );
 return $q->p( "@data" );
)
1;

Пример псевдо сценария:

use CGI;
use DBI;
use My::Module;

our $q = new CGI;
our $dbh = some_connenction_function($dsn);
our %conf = ( Foo => 1, Bar => 2, Random => some_dynamic_data() );

Я понимаю, что использование main:: namespace будет работать, но там будет чистым способом? Или нет?

3 ответа

package 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 я теперь создаю object instance который содержит эти переменные. И, по волшебству, я изменил свои функции на методы. Метод - это просто функция в вашем классе (aka module). Хитрость заключается в том, что мой экземпляр (переменная $mod_handler имеет все ваши обязательные переменные, хранящиеся красиво и аккуратно для вас. $mod_hander-> просто передает эту информацию для моих функций в мои функции, которые я имею в виду.

Итак, как выглядит ваш модуль? Давайте посмотрим на первую часть, где у меня есть конструктор, который является просто функцией, которая создает хранилище для моих переменных, которые мне нужны:

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 = {}; линия. Это создает ссылку на хэш. Если вы не понимаете ссылки, вы должны посмотреть на учебное пособие по Mark Reference, которое включено в Perldocs. В основном, ссылка - это ячейка памяти, в которой хранятся данные. В этом случае мой хэш не имеет имени, все, что у меня есть, - это ссылка на память, в которой он хранится, называется $self. В Perl нет ничего особенного в названии new или $self, но это стандарты, которые все в значительной степени следует.

Команда bless принимает мою ссылку, $self и объявляет ее типом My::Module. Таким образом, Perl может отслеживать, является ли тип $mod_handle типом экземпляра, который имеет доступ к этим функциям.

Как вы можете видеть, ссылка $self содержит все переменные, которые нужны мои функции. И я просто $mod_handle это обратно в свою основную программу, где храню ее в $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 для установки значений вашего участника.

Функция setter довольно проста. Шаблон выглядит следующим образом:

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->{} на $self->{} хеш непосредственно в этой программе, хорошая конструкция говорит, что я должен был использовать функции getter/setter следующим образом:

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 имеют представления о том, как эти параметры сохраняются. Единственное место, которое знает, как они хранятся, это сама функция getter/setter. Если я изменю структуру данных класса, мне не нужно ничего изменять, кроме самих функций getter/setter.

постскриптум

Здесь еда для размышлений...

Если все ваши вызовы SQL находятся в вашей функции My :: Module, почему бы просто не инициализировать $dbh и $q там в первую очередь. Таким образом, вам не нужно включать Use Dbi; модуль в вашей программе. Фактически, ваша программа теперь остается в блаженном невежестве точно, как хранятся данные. Вы не знаете, есть ли база данных SQL, база данных Mongo или даже некоторая плоская структура структуры db Berkeley.

Я включил модуль, который я сделал для работы, давно, когда я пытался упростить способ использования нашей базы данных. Этот модуль сделал несколько вещей:

  • Он обрабатывал все о базе данных. Инициализация и все ручки. Таким образом, вашей основной программе не нужно было использовать какой-либо модуль, кроме этого.
  • Он также отошел от разработчиков, которые пишут команды выбора. Вместо этого вы определили, какие поля вы хотели, и выяснили, как выполнить запрос для вас.
  • Он возвращает функцию incriminator, которая использовалась для извлечения следующей строки из базы данных.

Взгляните на это. Это не самое большое, но вы увидите, как я использую объектно-ориентированный дизайн, чтобы удалить все различные проблемы, которые у вас есть. Чтобы увидеть документацию, просто введите perldoc MFX:Cmdata в командной строке.


Одним из более чистых способов является использование Exporter для создания символов из других пространств имен, доступных в вашем пакете. Если вы просто случайный программист на Perl, я бы не стал с этим беспокоиться, потому что у него немного кривая обучения, есть много ошибок, а для игрушечного проекта это делает код еще более сложным. Но это важно для крупных проектов и для понимания модулей других людей.

Обычно в пакете есть глобальные переменные (или особенно глобальные функции), которые вы хотите использовать в другом модуле без его квалификации (т.е. Добавление package:: каждой переменной и вызов функции). Здесь один из способов, которым Exporter мог бы использовать для этого:

# MyModule.pm
package MyModule;
use strict; use warnings;
use Exporter;
our @EXPORT = qw($variable function);

our $variable = 42; # we want this var to be used somewhere else
sub function { return 19 }; # and want to call this function
...
1;

# my_script.pl
use MyModule;
$variable = 16; # refers to $MyModule::variable
my $f = function(); # refers to &MyModule::function

Спецификация проблемы обратная (не обязательно, что с ней что-то не так) - вы хотите, чтобы переменные основного скрипта и основного пакета были видимы внутри другого модуля. Для краткого списка переменных/функций, которые используются много раз, более чистый способ сделать это может заключаться в том, чтобы взломать таблицу символов:

package My::Module;

# required if you use strict . You do use strict , don't you?
our ($q, $dbh, %conf);

*q = \$main::q;
*dbh = \$main::dbh;
*conf = \%main::conf;
...

Теперь $My::Module::q и $main::q относятся к одной и той же переменной, и вы можете просто использовать $q в main или в пространстве имен My::Module.


  1. Использование main :: явно чистое.

  2. В качестве альтернативы вы можете передать эти данные в метод конструктора модуля, предполагая, что ваш модуль основан на объекте (или скопируйте его в собственные переменные модуля из main :: в конструкторе). Таким образом, не совсем перфективно-симпатичная главная :: скрыта от остальной части модуля.

licensed under cc by-sa 3.0 with attribution.