Что я должен сделать, если разработчик Американского лося метод перестал работать?

Что лучший способ состоит в том, чтобы обработать отказ в методе разработчика?

Например:

package MyObj;
use Moose;
use IO::File;

has => 'file_name'   ( is => 'ro', isa => 'Str',      required   =>1  );
has => 'file_handle' ( is => 'ro', isa => 'IO::File', lazy_build => 1 );

sub _build_file_handle {
    my $self = shift;
    my $fh = IO::File->new( $self->file_name, '<' );

    return $fh;
}

Если _build_file_handle сбои для получения дескриптора разработчик возвратится undef, который приводит ограничение типа к сбою.

Я мог использовать объединение в file_handle введите ограничение, так, чтобы оно приняло undef как допустимое значение. Но тогда, предикат has_file_handle возвратит true, даже когда значение undef.

Существует ли способ сигнализировать, что разработчик перестал работать, и атрибут должен остаться очищенным?

10
задан brian d foy 29 January 2010 в 20:26
поделиться

3 ответа

«Лучший» субъективный, но вам придется решить, что имеет больше смысла в вашем коде:

  1. , если вы можете продолжить в своем коде, когда файлHandle не выполняется (то есть Это извлекаемое условие), построитель должен вернуть UNDEF и установить ограничение типа , может быть, [IO :: file] '. Это означает, что вам также придется проверять определенность в этом атрибуте всякий раз, когда его использовать. Вы также можете проверить, был ли этот атрибут был построен правильно в Build , и выберите дальнейшее действие в этот момент (как Фридо намерено на его комментарий), например, Вызов clear_file_handle, если он undef (поскольку построитель всегда будет назначить значение атрибуту, предполагая, что он не умеет умереть).

  2. В противном случае пусть Builder проходит неудачу, либо путем явно выбрасывания исключения (который вы можете выбрать более высоким), либо просто возвращать UNDEF и позволить отключению сигналов типа. В любом случае, ваш код умрет; Вы просто получите выбор того, как он умирает и насколько это объемный след стека. :)

PS. Вы также можете посмотреть TRY :: Tiny , который Mouse использует внутренне, и это в основном просто обертка для * do Eval {Blah} или умереть .. IDIOM.

* Но сделано правильно! А в прохладный путь! (Кажется, я слышу много шепчущих в ухе от #moose ..)

6
ответ дан 3 December 2019 в 21:21
поделиться

Ты не думаешь на высоком уровне. Ладно, строитель терпит неудачу. Атрибут остается неопределенным. Но что делать с кодом, который вызывает аксессуар? В договоре класса указано, что вызов метода всегда будет возвращать IO::File. Но теперь он возвращает undef. (Контракт был IO::File, а не Может быть[IO::File], верно?)

Итак, в следующей строке кода вызывающий атрибут умирает ("Can't call method 'readline' on a undefined value at the_caller.pl line 42."), так как он ожидает, что ваш класс будет следовать контракту, который он определил. Неудача не должна была произойти с вашим классом, но теперь она произошла. Как может вызывающий абонент сделать что-нибудь, чтобы исправить эту проблему?

Если он может справиться с undef, то вызывающему абоненту на самом деле не нужен файл-хэндл, чтобы начать с... так почему он попросил об этом ваш объект?

С учетом этого, единственное разумное решение - это умереть. Вы не можете выполнить договор, на который согласились, и умереть - это единственный способ выбраться из этой ситуации. Так что просто сделайте это; смерть - это факт жизни.

Теперь, если вы не готовы умереть, когда строитель работает, вам нужно будет изменить код, который может провалиться. Это можно сделать во время сборки объекта, либо сделав его неластичным, либо явно оживив атрибут в BUILD (BUILD { $self->file_name }).

Лучше вообще не выставлять хэндл файла во внешний мир, а делать что-то вроде:

# dies when it can't write to the log file
method write_log {
    use autodie ':file'; # you want "say" to die when the disk runs out of space, right?
    my $fh = $self->file_handle;
    say {$fh} $_ for $self->log_messages;
}

Теперь вы знаете, когда программа умрёт; в new, или в write_log. Вы знаете, потому что так написано в документах.

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

И, умирание в write_log может даже быть чем-то, от чего вы можете восстановиться (в блоке catch), в то время как "не смог открыть эту случайную непрозрачную вещь, о которой вы все равно не должны знать", гораздо сложнее для вызывающего абонента восстановиться.

В принципе, создайте свой код в здравом уме, и исключения - это единственный ответ.

(В любом случае, я не понимаю, что такое "они - кладж". Они работают точно так же на Си++ и очень похожи на Java и Haskell и на всех других языках. Неужели слово die действительно настолько страшно или что-то в этом роде?)

.
9
ответ дан 3 December 2019 в 21:21
поделиться

Есть ли способ сигнализировать о том, что строителю не удалось, и атрибут должен остаться очищенным?

Нет. Это не имеет смысла, строитель будет стрелять, если атрибут очищен, если он был очищен внутри строителя, он просто будет стрелять, когда вы сделаете следующий вызов к нему, и останется в очищенном состоянии. Пустая трата времени, только на set-something-if-it-works-and-if-not-continue .

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

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

sub get_fh {                                                                
  my $self = shift;                                                         

  my $abs_loc = $self->abs_loc;                                             

  if ( !(-e $abs_loc) || -e -z $abs_loc ) {                                 
    $self->error({ msg => "Critical doesn't exist or is totally empty" });  
    die "Will not run this, see above error\n";                             
  }                                                                         

  my $st = File::stat::stat($abs_loc);                                      
  $self->report_datetime( DateTime->from_epoch( epoch => $st->mtime ) );    

  my $fh = IO::File->new( $abs_loc, 'r' )                                   
    || die "Can not open $abs_loc : $!\n"                                   
  ;                                                                         

  $fh;                                                                      

}                                                                           

Совершенно другой подход - это подкласс IO::File , с мета-данными о файле, который вы хотите сохранить. Иногда это эффективно, и отличное решение:

package DM::IO::File::InsideOut;
use feature ':5.10';
use strict;
use warnings;

use base 'IO::File';

my %data;

sub previouslyCreated {
  $data{+shift}->{existed_when_opened}
}

sub originalLoc {
  $data{+shift}->{original_location}
}

sub new {
  my ( $class, @args ) = @_;

  my $exists = -e $args[0] ? 1 : 0;

  my $self = $class->SUPER::new( @args );

  $data{$self} = {
    existed_when_opened => $exists
    , original_location => $args[0]
  };

  $self;

};
2
ответ дан 3 December 2019 в 21:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: