Как я должен обрабатывать ошибки в методах Perl, и что я должен возвращать из методов? - PullRequest
5 голосов
/ 10 января 2009

Я обернул Perl Net :: SSH :: Expect небольшим модулем, чтобы уменьшить стандартный код, необходимый для написания нового скрипта конфигурации для использования с нашими картами HP iLO, Хотя, с одной стороны, я хочу, чтобы эта обертка была как можно более компактной, чтобы ее могли использовать коллеги, не являющиеся программистами, я также хочу, чтобы она была написана как можно лучше.

Используется так:

my $ilo = iLO->new(host => $host, password => $password);
$ilo->login;

$ilo->command("cd /system1");
$ilo->command("set oemhp_server_name=$system_name", 'status=0');

и это iLO::command():

sub command {
    my ($self, $cmd, $response) = @_;

    $response = 'hpiLO-> ' unless defined($response);

    # $self->{ssh} is a Net::SSH::Expect object
    croak "Not logged in!\n" unless ($self->{ssh});

    $self->{ssh}->send($cmd);
    if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) {
        return {
            before => $self->{ssh}->before(),
            match => $self->{ssh}->match(),
            after => $self->{ssh}->after(),
        };
    } else {
        carp "ERROR: '$cmd' response did not match /$response/:\n\n",
            $self->{ssh}->before()),
            "\n";
        return undef;
    }
}

У меня есть два связанных запроса. Во-первых, как мне поступить с ответами, которые не соответствуют ожидаемому ответу? Я думаю, что то, что я делаю сейчас, удовлетворительно - возвращая undef, я сообщаю, что что-то сломалось, и мой croak() выдаст ошибку (хотя вряд ли изящно). Но это похоже на запах кода. Если бы в Perl были исключения, я бы поднял их и позволил вызывающему коду решить, игнорировать ли их / выйти / напечатать предупреждение, но это не так (ну, в 5.8). Возможно, мне следует вернуть какой-то другой объект (iLO::response или что-то в этом роде), который содержит сообщение об ошибке и содержимое $ilo->before() (что является просто Net :: SSH :: Expect's before())? Но если я сделаю это - и мне придется обернуть каждый $ilo->command в тесте, чтобы поймать его - мои сценарии снова будут заполнены образцом.

Во-вторых, что я должен вернуть для успеха? Опять же, мой хеш, более или менее содержащий ответ от Net :: SSH :: Expect, выполняет свою работу, но он как-то не выглядит «правильным». Хотя этот пример на Perl, мой код на других языках выдает тот же знакомый запах: я никогда не уверен, что и как вернуть из метода. Что ты можешь мне сказать?

Ответы [ 4 ]

5 голосов
/ 10 января 2009

Если вы знакомы с исключениями из языков, таких как Java, тогда думайте о die как throw и eval как try и catch. Вместо того, чтобы возвращать undef, вы можете сделать что-то вроде этого:

if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) {
    return {
        before => $self->{ssh}->before(),
        match => $self->{ssh}->match(),
        after => $self->{ssh}->after(),
    };
}

die "ERROR: '$cmd' response did not match /$response/:\n\n" 
. $self->{ssh}->before();

Тогда в вашем телефонном коде:

eval { 
    $ilo->command("set oemhp_server_name=$system_name", 'status=0');
};

if ( my $error = $@ ) { 
    # handle $error here
}

Как и исключения в других языках, это позволяет вам выходить из подметода в любой точке, не беспокоясь о распространении возвращаемых значений вверх по стеку вызовов. Они будут пойманы первым блоком eval, который их найдет. Кроме того, вы можете die снова выбросить исключение, с которым вы не можете справиться, сделайте резервную копию стека.

Более того, вы можете использовать die для создания объекта, который ваш обработчик исключений может запросить для получения полезной информации и сообщений об ошибках. Мне нравится использовать Exception :: Class для этой цели. Модуль Error предоставляет некоторый синтаксический сахар для выполнения Java-подобных блоков try / catch.

5 голосов
/ 10 января 2009

Обычный способ вызвать исключения в Perl - die . Обычный способ поймать их - использовать eval с блоком в качестве аргумента и тестирование $ @ после завершения eval.

4 голосов
/ 10 января 2009

Вы найдете множество обсуждений такого рода вещей в googlespace. Лучшая практика, независимо от того, что вы решите, - не перегружать ни одно из значений, поэтому возвращаемое значение означает разные вещи. Это всегда должен быть код ошибки или код ошибки. Люди не должны смотреть на фактическое значение, чтобы решить, является ли это кодом ошибки или нет.

Проверьте популярные модули Perl на CPAN (или те, которые вы уже используете), чтобы увидеть, что они делают. Я даже немного об этом говорю в Мастеринг Perl , но я не даю черно-белого ответа. Как и во всем реальном коде, реальный ответ - «Это зависит».

Есть много разных способов сделать это. К сожалению, это означает, что люди делают это всеми способами. Поскольку дело обстоит именно так, я призываю консистенцию в качестве первостепенного правила. Что большая часть кода уже делает (не считая неправильного пути)? Если мне нужно вписаться в существующую кодовую базу, я стараюсь использовать тот же интерфейс, который уже используется большей частью кода.

Если нет явного победителя, напишите варианты использования, используя несколько разных стилей. Какой из них лучше подходит для решения проблемы или более естественно отражает шаги, предпринимаемые большинством пользователей? Это не всегда только чтение для die и eval. Напишите примеры сценариев, используя ваши нереализованные интерфейсы. Какой стиль вы собираетесь использовать? Я обнаружил, что на самом деле написание сценария до того, как я реализовал интерфейс, показывает мне гораздо больше, чем я думал. Если я пишу материал для использования другими людьми, я показываю им сценарии в разных стилях и спрашиваю, какой из них им нравится больше.

И, если все это не удастся, дотянитесь до 2d6. :)

0 голосов
/ 10 января 2009

Помимо использования «die» в качестве исключения, вы также можете добавить другой метод:

if (!$ilo->commandSucceeded("set oemhp_server_name=$system_name", 'status=0')) {
     #recover here
}

Конечно, внутренняя реализация команды () становится

die ... if !commandSucceeded;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...