2011-08-15 21 views
9

Jaki jest najlepszy sposób na poradzenie sobie z wyjątkami rzucił w łańcuchu metody w Perlu? chcę przypisać wartość 0 lub undef jeśli któryś z metod przykuty wyjątekJak radzić sobie z błędami w łańcuchach metod w Perlu?

przykładowy kod:

my $x = $obj->get_obj->get_other_obj->get_another_obj->do_something; 

Co najlepszy sposób to zrobić? Czy za każdym razem muszę zawijać instrukcję try/catch/finally? Kontekst, który chcę zastosować to: Pracuję w programowaniu stron internetowych przy użyciu Catalyst i DBIC i robię wiele połączonych zestawów wyników i jeśli niektóre z tych zestawów wyników rzucają wyjątek, chcę tylko przypisać wartość 0 lub undef, a następnie leczyć ten błąd w szablonie (jestem przy użyciu Template Toolkit). Jeśli jest inny sposób, aby to zrobić bez zawijania każdego połączenia w próbie/catch, proszę dać mi znać. Jeśli znasz lepszy sposób traktowania tego typu błędów w tym samym kontekście (Catalyst/DBIC/TT), sugeruj. Praktycznym przykładem może być sytuacja, gdy użytkownik szuka czegoś, a to nie istnieje.

Odpowiedz

8

Zajmuję się tym, zwracając obiekt zerowy w momencie awarii. Obiekt ten reaguje na każdą metodę, po prostu zwracając się, więc nadal to robi, dopóki nie pożre pozostałych metod. Na koniec spójrz na numer $x, aby sprawdzić, czy jest to oczekiwany wynik, czy też ten obiekt zerowy.

Oto przykład coś takiego:

use v5.12; 

package Null { 
    my $null = bless {}, __PACKAGE__; 
    sub AUTOLOAD { $null } 
    } 

Dla każdej metody nazywa, Przechwytuje autoLoad i zwraca pusty obiekt.

Po napotkaniu błędu zwróci się jeden z tych obiektów zerowych. W środku łańcucha metod wciąż otrzymujesz obiekt, więc Perl nie wysadza się, gdy wywołujesz następną metodę.

sub get_other_obj { 
    ...; 
    return Null->new if $error; 
    ...; 
    } 

Na końcu łańcucha możesz sprawdzić, co otrzymałeś, aby zobaczyć, czy jest to obiekt zerowy. Jeśli to masz, stało się coś złego.

To podstawowy pomysł. Możesz poprawić klasę Null, aby zapamiętywać wiadomość i miejsce jej utworzenia, lub dodać kilka polimorficznych metod (takich jak sub is_success { 0 }), aby ładnie odtwarzać się z interfejsami obiektów, które spodziewałeś się uzyskać.

Myślałem, że napisałem coś o tym gdzieś, ale teraz nie mogę go znaleźć.

+0

Jeden problem z tym: metody Settera będą używać wartości pustej do określenia zwrotu wartość czynszu. Na przykład '$ foo-> Name (" David ") ustawi nazwę na" David ", a' $ foo-> Name' zwróci obecną nazwę. Zatem zerowy zwrot z jednej metody może być poprawnym wprowadzeniem do innej metody. –

+0

To nie jest problem. To nie jest operacja. Następne metody nic nie robią. Nie przekazujesz obiektu zerowego jako argumentu; to jest referent. Jeśli poprzednia metoda nie zwraca obiektu, nie można go połączyć. –

+0

Przepraszam, że nie rozumiem. Jak mogę zastosować go w zestawach wyników łańcuchów DBIC? – nsbm

-1

Jednym z pomysłów byłoby stworzenie klasy, która używa overload do zwracania fałszywej wartości, gdy obiekt instancji jest oceniany w kontekście łańcuch/liczba/boolean, ale nadal pozwala na wywoływanie metod na tym obiekcie. Metoda AUTOLOAD może zawsze zwrócić $self, co pozwala łańcuchowi metod propagacji tego samego błędu.

2

można napisać metodę skalarne, które będą owijać łańcucha metoda w obsługę błędów:

my $try = sub { 
    @_ > 1 or return bless {ok => $_[0]} => 'Try'; 

    my ($self, $method) = splice @_, 0, 2; 
    my $ret; 
    eval { 
     $ret = $self->$method(@_); 
    1} or return bless {error => [email protected]} => 'Try'; 
    bless {ok => $ret} => 'Try' 
}; 

{package Try; 
    use overload fallback => 1, '""' => sub {$_[0]{ok}}; 
    sub AUTOLOAD { 
     my ($method) = our $AUTOLOAD =~ /([^:]+)$/; 
     $_[0]{ok} ? $_[0]{ok}->$try($method, @_[1..$#_]) : $_[0] 
    } 
    sub DESTROY {} 
    sub error {$_[0]{error}} 
} 

go używać:

{package Obj; 
    sub new {bless [0]} 
    sub set {$_[0][0] = $_[1]; $_[0]} 
    sub add {$_[0][0] += ($_[1] || 1); $_[0]} 
    sub show {print "Obj: $_[0][0]\n"} 
    sub dies {die "an error occured"} 
} 

my $obj = Obj->new; 

say "ok 1" if $obj->$try(set => 5)->add->add->show; # prints "Obj 7" 
                # and "ok 1" 

say "ok 2" if $obj->$try('dies')->add->add->show; # prints nothing 

say $obj->$try('dies')->add->add->show->error; # prints "an error occured..." 

Pierwsza linia metody $try pozwala również następującą składnię :

say "ok 3" if $obj->$try->set(5)->add->add->show; 
Powiązane problemy