Изящное ветвление или мощь Perl с ООП.

| Комментариев: 2 | Нет трекбэков

Задача когда вы обрабатываете всевозможные входные опции в виде:

if($param eq „new") {
print „new";
}

if($param eq „some") {
print „some";
}

Довольно распрастраненая, особенно в скриптах для web. В известной книге "Сборник рецептов", так называемый Perl Cookbook приводится одно известное решение.

Это решение основано на создании хэша, с именами функций в значениях ключей. Приведу пример из cookbook, так как лучше один раз увидеть:

   1    #!/usr/bin/perl -w
   2    use CGI qw(param header);
   3    
   4    my $page=param('page');
   5    
   6    %States = (
   7        'Default' => \&front_page,
   8        'Shirt' => \&shirt,
   9        'Sweater' => \&sweater,
  10        'Checkout' => \&checkout,
  11        'Card' => \&credit_card,
  12        'Order' => \&order,
  13        'Cancel' => \&front_page
  14    );
  15    
  16    if ($States{$page}) {
  17    $States{$page}->(); # Вызвать нужную подпрограмму
  18    } else {
  19    no_such_page():
  20    }  

В зависимости от переданного в параметре page значения - вызывается соответствующая подпрограмма. Чем больше у нас подпрограмм, тем больше приходится писать в хэш %States. И хотя в cookbook это решение противопоставляется громоздкой конструкции if .. else ... есть более красивое решение, с использованием возможностей ООП в Perl.

Зачем собственно нам нужен хэш %States? Гораздо лаконичней и изящней сделать все через вызов методов класса (пакета). Для начала код самого скрипта:

   1    #!/usr/bin/perl -w
   2    use CGI qw(param header);
   3    use MyClass;
   4    
   5    my $act=MyClass->new();
   6    my $method=param('page');
   7    
   8    if($act->can($method)) {
   9        $act->$method;
  10    } else {
  11        $act->default;
  12    }

Далее мы просто добавляем методы в пакет MyClass и не обслуживаем никакие хэши. Пример пакета:

   1    package MyClass;
   2    
   3    sub new {
   4        my $class = shift;
   5        my $self = bless {}, $class;
   6        return $self;
   7    }
   8    
   9    # стартовая страница
  10    sub shirt {
  11        my $self=shift;
  12        # ... do something
  13    }
  14    
  15    sub default {
  16       my $self=shift;# имя класса 
  17    }

Можно было обойтись и без проверки can (из псевдокласса UNIVERSAL), тогда код самого скрипта был бы еще лаконичней, мы бы сразу вызывали $act->$method, но для случаев когда передан несуществующий метод, надо было добавить в наш класс метод AUTOLOAD, который запускал бы программу (метод) по умолчанию. Для реализации AUTOLOAD объявите в начале пакета переменную через our и добавьте метод:

   1    package MyClass;
   2    
   3    our $AUTOLOAD;
   4    
   5    sub AUTOLOAD {
   6    my $self = shift;
   7    return if our $AUTOLOAD=~/::DESTROY$/;
   8    $self->default;
   9    }

Важно - необходимо сразу делать возврат из AUTOLOAD, если происходит вызов деструктора (DESTROY).

Вариант с автозагрузкой (AUTOLOAD), конечно лаконичней, но чуть чуть медленней чем вариант с UNIVERSIAL::can, так как интерпретатор Perl проверяет сперва методы через UNIVERSAL и лишь затем вызывает AUTOLOAD, но в большинстве случаев - это не так принципиально.

Важное замечание, поступающие данные для инициализации $method нужно очищать. При этом важно очищать их от имени конструктора класса, в нашем примере - таким ялвяется new, иначе будет ошибка скрипта. К примеру:

$method=~s/^new$//i;# спасибо xorro за поправку

еще будет полезно очищать метод от символов :: , иначе кто-нибудь может попытаться вызвать конструкции типа: SUPER::new. Как окончательное решение - если имена методов содержат только символы в диапазоне a-z, то чистим так:

$method=~/[^a-z]+//g;

ну и так далее... НО, как говорят такая очистка убивает красоту приема, поэтому немного модифицируем конструктор MyClass:

   1    package MyClass;
   2    
   3    sub new { 
   4           my $class = shift;
   5           return $class->default if(ref $class);# проверка!
   6           my $self = bless {}, $class; 
   7           return $self; 
   8    } 

Теперь переменную $method можно не проверять, так как если будет вызван конструктор new второй раз, то переменная $class будет содержать ссылку, и мы это легко определим и вызовем метод по умолчанию.

 Думаю представленное мной решение обратит ваше внимание на возможности ООП в Perl еще раз.

P.S.

Кстати Java, насколько я знаю, так не может ;-)

Нет трекбэков

URL для трекбэков: http://perlmonks.org.ru/cgi-bin/MT/engine/mt-tb.cgi/14

Комментариев: 2

$method=~s/new//i; - здесь похоже ошибка. Ведь тогда, к примеру, 'new_user' превратится в '_user', более того, если передать 'newnew', то может быть вызван и метод new... Нужно так: $method =~ s/^new$//;

Внес изменения - спасибо за обсуждение на ру_перл!

Комментировать

Об этой записи

Сообщение опубликовано 18.04.2009 20:33. Автор — Monks.

Предыдущая запись — Grab-killer или убиваем граберы контента.

Следующая запись — Tomcat и Perl. Быстрый старт

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.

Страницы


 


 

Page copy protected against web site content infringement by Copyscape