Качество имеет значение.
В программах есть ошибки. Программы требуют поддержки и расширения. Над программами работает множество программистов.
Хорошее программирование требует от нас находить баланс между выполнением работы и созданием условий для того, чтобы выполнять её успешно и в будущем. Мы можем менять друг на друга время, ресурсы и качество. То, насколько хорошо мы это делаем, определяет наш уровень как практичных профессионалов.
Понимание Perl важно. Так же как и развитие чувства хорошего вкуса. Единственный способ этого добиться — практиковаться в поддержке кода и в чтении и написании хорошего кода. На этом пути нет срезов, но есть направляющие ориентиры.
Поддерживаемость — туманная мера простоты понимания и изменения существующей программы. Оставьте какой-нибудь код в стороне на шесть месяцев, а затем снова к вернитесь нему. Поддерживаемость определяет сложность, с котороый вы встретитесь при внесении изменений.
Поддерживаемость — не вопрос синтаксиса, как и не мера того, как ваш код будет выглядеть для непрограммиста. Представим компетентного программиста, понимающего суть задачи, которую код должен решать. С какими проблемами он встретится при корректном исправлении бага или добавлении улучшений?
Способность писать поддерживаемый код приходит из заработанного тяжёлым трудом опыта и поддерживается умелым обращением с идиомами, техниками и преобладающим стилем языка. Но даже новички могут улучшить поддерживаемость своего кода, следуя нескольким принципам:
Хорошо спроектированные системы содержат мало дублирования. Они используют функции, модули, объекты и роли для выделения дублирующегося кода в пригодные для повторного использования компоненты, которые точно моделируют предметную область задачи. Самый лучший дизайн позволяет вам добавлять возможности, удаляя код.
Некоторые задачи требуют изощрённых решений. Инкапсулируйте этот код за простым интерфейсом и документируйте свою изощрённость.
Всё это не может служить извинением попыток увильнуть от обработки ошибок, или модульности, или валидации, или безопасности. Простой код может использовать продвинутые возможности. Простой код может применять огромные залежи CPAN-модулей. Простой код может требовать некоторого труда для его понимания. Однако простой код решает проблемы эффективно, без выполнения ненужной работы.
Иногда вам требуется мощный, надёжный код. Иногда вам требуется однострочник. Простота означает понимание разницы и создание только того, что вам нужно.
Perl свободно заимствует у других языков. Perl позволяет вам писать код так, как вы хотите его писать. Программисты на C зачастую пишут на Perl в стиле C, также как Java-программисты пишут на Perl в стиле Java. Эффективные Perl-программисты пишут на Perl в стиле Perl, включая применение идиом языка.
CPAN-разработчики, Perl-монгеры и участники списков рассылок обладают заработанным тяжёлым трудом опытом в решении задач мириадами разных способов. Говорите с ними. Читайте их код. Задавайте вопросы. Учитесь у них и давайте им учиться у вас.
Perl::Critic
), переформатирование (Perl::Tidy
) и частные системы распространения (CPAN::Mini
). Пользуйтесь инфраструктурой CPAN; следуйте модели CPAN в написании, документировании, сборке, тестировании и распространении вашего кода.Поддерживаемость — в конечном счёте, вопрос дизайна. Хороший дизайн приходит с практикой полезных привычек:
Если вы найдёте баг, сообщите о нём. Предложите патч, если возможно. Исправьте опечатку. Попросите реализовать возможность. Скажите «Спасибо!». Вместе мы лучше, чем по отдельности. Мы сильны и эффективны, если повторно используем код.
Когда вы готовы, когда вы решили новую задачу, поделитесь решением. Присоединяйтесь к нам. Мы решаем задачи.
Программировать хорошо означает предвидеть неожиданное. Файлы, которые должны существовать, не существуют. Этот огромный диск, который никогда не заполнится, оказывается полным. Всегда доступная сеть не доступна. Неломающиеся базы данных ломаются. Исключения случаются, и надёжное программное обеспечение должно их обрабатывать. Если вы можете восстановиться, отлично! Если не можете, запишите нужную информацию в лог и повторите попытку.
Pelr 5 обрабатывает исключительные ситуации с помощью исключений: механизм потока управления динамической области видимости, разработанный для генерации и обработки ошибок.
Предположим, вы хотите записать в лог файл. Если вы не смогли открыть файл, что-то пошло не так. Используйте die
чтобы выбросить исключение:
sub open_log_file
{
my $name = shift;
open my $fh, '>>', $name
or die "Can't open logging file '$name': $!";
return $fh;
}
die()
устанавливает значение своего операнда в глобальную переменную $@
и немедленно выходит из текущей функции, ничего не возвращая. Это выброшенное исключение будет подниматься по стеку вызовов (Контроллируемое выполнение) до тех пор, пока не будет где-нибудь поймано. Если исключение не будет поймано нигде, программа завершится с ошибкой.
Обработка исключений использует ту же самую динамическую область видимости (Динамическая область видимости.), что и символы, объявленные с помощью local
.
Иногда исключение, приводящее к выходу из программы, удобно. Программа, выполняемая как синхронизированный процесс, может выбросить исключение, если логи ошибок заполнены, что приведёт к отправке SMS администраторам. Однако, не все исключения должны быть фатальными. От некоторых из них хорошие программы должны уметь восстанавливаться, или по крайней мере сохранять текущее состояние и завершаться чисто.
Используйте блочную форму оператора eval
чтобы поймать исключение:
# лог-файл может не открыться
my $fh = eval { open_log_file( 'monkeytown.log' ) };
Если файл успешно открыт, $fh
будет содержать дескриптор файла. Если нет, $fh
останется неопределённой и выполнение программы продолжится.
Блочный аргумента eval
вводит новую область видимости, и лексическую, и динамическую. Если open_log_file()
вызывает другие функции, и какая-нибудь из них в конечном счёте выбросила исключение, этот eval
его поймает.
Обработка исключений — грубый инструмент. Он будет ловить все исключения в своей динамической области видимости. Чтобы проверить, какое исключение вы поймали (и поймали ли вообще), проверьте значение $@
. Убедитесь, что локализовали $@
с помощью local
, прежде чем попытаться поймать исключение; помните, что $@
— глобальная переменная:
local $@;
# лог-файл может не открыться
my $fh = eval { open_log_file( 'monkeytown.log' ) };
# поймано исключение
if (my $exception = $@) { ... }
Сразу же скопируйте $@
в лексическую переменную, чтобы избежать возможности того, что последующий код перезапишет глобальную переменную $@
. Вы никогда не знаете, что ещё могло использовать блок eval
в каком-нибудь другом месте и сбросить $@
.
$@
обычно содержит строку, описывающую исключение. Проверьте её содержимое, чтобы увидеть, можете ли вы обработать исключение:
if (my $exception = $@)
{
die $exception
unless $exception =~ /^Can't open logging/;
$fh = log_to_syslog();
}
Пробросьте исключение, снова вызвав die()
. Передайте существующее исключение или новое, в зависимости от необходимости.
Применение к строковым исключениям регулярных выражений может быть ненадёжным, потому что сообщения об ошибках могут со временем меняться. Это относится и к внутренним исключениям, которые выбрасывает сам Perl. К счастью, вы можете также передавать в die
ссылку — даже благословлённую ссылку. Это позволяет вам предоставлять в вашем исключении гораздо больше информации: номера строк, файлы и другую отладочную информацию. Получение этой информации из чего-то структурированного намного легче, чем разбор строки. Ловите эти исключения так же, как любые другие.
CPAN-дистрибутив Exception::Class
упрощает создание и использование объектов-исключений:
package Zoo::Exceptions
{
use Exception::Class
'Zoo::AnimalEscaped',
'Zoo::HandlerEscaped';
}
sub cage_open
{
my $self = shift;
Zoo::AnimalEscaped->throw
unless $self->contains_animal;
...
}
sub breakroom_open
{
my $self = shift;
Zoo::HandlerEscaped->throw
unless $self->contains_handler;
...
}
Хотя выбрасывание исключений относительно просто, ловить их сложнее. Корректное использование $@
требует от вас обходить некоторые неочевидные риски:
local
использование дальше в динамической области видимости может модифицировать $@
DIE
) может изменять $@
eval
и изменять $@
Perl 5.14 исправил некоторые из этих проблем. Признаем, они встречаются очень редко, но зачастую сложны для диагностики и исправления. CPAN-дистрибутив Try::Tiny
улучшает безопасность обработки исключений и синтаксис (footnote: На самом деле, Try::Tiny
помог вдохновить на улучшения в обработке исключений в Perl 5.14.).
Try::Tiny
прост в использовании:
use Try::Tiny;
my $fh = try { open_log_file( 'monkeytown.log' ) }
catch { log_exception( $_ ) };
try
заменяет eval
. Необязательный блок catch
выполняется, только если try
поймал исключение. catch
получает пойманное исключение как переменную-топик $_
.
Сам Perl 5 выбрасывает несколько исключительных условий. perldoc perldiag
перечисляет несколько «отлавливаемых фатальных ошибок». Тогда как часть из них — синтаксические ошибки, выбрасываемые в процессе компиляции, другие вы можете поймать во время выполнения. Наиболее интересны следующие:
Конечно, вы также можете ловить исключения, производимые autodie
(Прагма autodie), и любые лексические предупреждения, повышенные до исключений (Регистрация своих собственных предупреждений).
Большинство расширений Perl 5 — модули, предоставляющие новые функции или определяющие классы (Moose). Некоторые же модули вместо этого влияют на поведение самого языка, как strict
или warnings
. Такие модули называют прагмами. По соглашению прагмы имеют имена в нижнем регистре, чтобы отличить их от других модулей.
Прагмы работают посредством экспорта определённого поведения или информации в лексические области видимости вызывающего их кода. Так же как объявление лексической переменной делает символическое имя доступным в пределах области видимости, использование прагм делает их поведение действующим в пределах этой области видимости:
{
# $lexical невидима; strict не действует
{
use strict;
my $lexical = 'available here';
# $lexical видима; strict действует
...
}
# $lexical снова невидима; strict не действует
}
Также как лексические объявления воздействуют на внутренние области видимости, прагмы сохраняют свой эффект во внутренних областях видимости:
# область видимости файла
use strict;
{
# внутренняя область видимости, но strict всё ещё действует
my $inner = 'another lexical';
...
}
Подключайте прагмы с помощью use
, как и любой другой модуль. Прагмы принимают аргументы, такие как минимальный номер используемой версии и список аргументов для изменения поведения прагмы:
# требует объявления переменных, запрещает голые слова
use strict qw( subs vars );
Иногда вам нужно отключить все или часть этих эффектов в глубже вложенной лексической области видимости. Встроенная директива no
отменяет импорт (Импорт), что отменяет эффекты правильно работающих прагм. Например, так можно отключить защиту strict
, если вам нужно сделать что-нибудь символьное:
use Modern::Perl;
# или use strict;
{
no strict 'refs';
# здесь можно манипулировать символьной таблицей
}
Perl 5.10.0 добавил возможность писать свои собственные лексические прагмы в виде кода на чистом Perl. perldoc perlpragma
объясняет как это делать, а описание $^H
в perldoc perlvar
объясняет, как эта возможность работает.
Но и до 5.10 Perl 5 включал несколько полезных встроенных прагм.
strict
включает проверку компилятором символических ссылок, использования голых слов и объявлений переменных.warnings
включает опциональные предупреждения о нерекомендуемых, непреднамеренных и неудачных поведений.utf8
заставляет парсер воспринимать исходный код как имеющий кодировку UTF-8.autodie
включает автоматическую проверку ошибок системных вызовов и встроенных функций.constant
позволяет вам создавать константные значения времени компиляции (см. Const::Fast
CPAN в качестве альтернативы).vars
позволяет вам объявлять глобальные переменные пакета, такие как $VERSION
или @ISA
(Благословлённые ссылки).feature
позволяет вам отдельно включать и отключать возможности Perl 5, появившиеся после 5.10. Как use 5.14;
включает все возможности Perl 5.14 и прагму strict
, так и use feature ':5.14';
делает то же самое. Эта прагма более полезна для отключения отдельных возможностей в лексической области видимости.less
демонстрирует, как написать прагму.CPAN начал собирать невстроенные прагмы:
autobox
включает объектоподобное поведение для встроенных типов Perl 5 (скаляров, ссылок, массивов и хешей).perl5i
собирает и включает многие экспериментальные расширения языка в одно целое.autovivification
отключает автовивификацию (Автовивификация)indirect
предотвращает использование непрямых вызовов (Непрямые объекты)Эти инструменты пока не имеют широкого использования. Два последних могут помочь вам писать более корректный код, тогда как с двумя предыдущими стоит поэкспериментировать в небольших проектах. Они показывают, чем мог бы быть Perl 5.