Если функция выполняет несколько разных, слабо связанных друг с другом задач, подумайте о том, чтобы разбить эту функцию на несколько.
Наименования функций должны включать глагол, например «get_domain_name», или «crash_my_program».
Имена, заданные без учёта этого принципа, вроде «flat_components», могут быть истолкованны совершенно по разному, например как «get_flat_components», «set_flat_components», «update_flat_components», «remove_flat_components» или «add_flat_components».
В книге Стива Макконелла «Совершенный код» приводятся устоявшиеся пары антонимов, рекомендуемые для использования в именах функций/методов, а именно:
add / remove, begin / end, create / destroy, first / last, increment / decrement, insert / delete, lock / unlock, min / max, next / prev, old / new, open / close, show / hide, source / target, start / stop, up / down. |
Использование «несогласованных» пар глаголов вроде add / delete или insert / destroy вводит в заблуждение и усложняет анализ кода.
Если в модуле присутствуют функции, предназначенные только для внутреннего использования, которые никогда не будут вызваны за пределами модуля (за исключением случая автоматического тестирования), можно предварять имена этих private-функций знаком подчёркивания. Пример: _do_some_private_actions.
Функции отделены друг от друга минимум одной пустой строкой. Для каждой функции необходимо краткое описание того, что она делает:
# Получить имя домена по его id sub get_domain_name { … } |
или так,
=item B<get_domain_name>($id) |
Получить имя домена по его id
=cut sub get_domain_name { … } |
Параметры принимаются с использованием конструкции:
my ($param1, $param2) = @_; |
Она должна располагаться в первой строке функции и отделяться пустой строкой от последующего кода. Этот вариант считается стандартным и должен применяться всегда, кроме описанных ниже исключений.
Возможно использование shift и pop в сложных случаях, где это обоснованно (т. е. требуется изменение @_, либо используется хитрая последовательность параметров): некоторые случаи использования AUTOLOAD, работы с коллбэками и т. п.
В случае переменного количества параметров, если количество и смысл последующих параметров может варьироваться в зависимости от значения предыдущих параметров, тоже допускается принимать эти первые по счёту параметры с помощью shift:
my $action = shift; my ($name, $value, $reason); ($name, $value) = @_ if $action eq 'new'; ($reason) = @_ if $action eq 'destroy'; |
Однако такие ситуации очень редко имеют весомое оправдание, как правило от них стоит избавляться, изменив интерфейс функции либо использую именованные параметры:
my ( $action, $params ) = @_; if ( $action eq 'new' ) { # работаем с $params->{name} и $params->{value} } elsif ( $action eq 'destroy') { # работаем с $params->{reason} } |
Допускается работа с аргументами функции напрямую через $_[0], когда это явно требуется с точки зрения логики работы функции или производительности, например:
Все случаи отклонения от стандартного вида приёма параметров должны иметь явное обоснование, которое желательно описать в комментарии.
Именованные аргументы принимаются следующим образом:
my %param = @_; |
Если аргументы функции нетривиальны (их назначение и набор допустимых значений не очевидны из их названия и контекста), после описания функции добавляется строка пояснения по аргументам. Если какой-либо аргумент может принимать конечное дискретное множество значений — это множество также перечисляется в комментариях. Если используются именованные параметры — все возможные параметры также перечисляются в комментариях, обязательные параметры обозначаются звёздочкой:
# %param: action*, domain_name*, comment # action: new, renew, delete |
Функция может принимать не более 2-х, максимум 3-х ПОЗИЦИОННЫХ аргументов. При большем количестве аргументов либо проводится рефакторинг с целью уменьшения количества входных параметров, либо используются именованные параметры:
my %param = @_; |
Именованные параметры обязательно документируются.
В случае использования именованных параметров, если есть как обязательные, так и необязательные параметры, желательно проверять наличие обязательных параметров. В случае их отсутствия — вызывать исключение с помощью Carp::confess (для получения stack backtrace) или Carp::croak. В случае использования позиционных параметров проверка наличия и корректности переданных аргументов также приветствуется.
Если выходные параметры функции не очевидны / нетривиальны, особенно это касается возврата сложных структур данных, желательно их документировать (после описания входных параметров).
Если функция возвращает список значений, но из возвращаемого списка часто может использовать только первое значение, желательно выдавать только первое значение или весь список в зависимости от контекста:
return wantarray ? (user_id, username) : user_id; |