Nie myślisz o poziomie wystarczająco wysokim. OK, budowniczy nie działa. Atrybut pozostaje niezdefiniowany. Ale co zrobić z kodem wywołującym akcesor? Umowa klasy wskazywała, że wywołanie metody zawsze zwróci plik IO ::. Ale teraz wraca undef. (Umowa była IO::File
nie Maybe[IO::File]
, prawda?)
więc w następnej linii kodu, rozmówca będzie die ("Nie można nazwać metodą«readline»na nieokreślonej wartości w the_caller.pl linii 42 "), ponieważ oczekuje, że twoja klasa będzie postępować zgodnie z ustaloną przez nią umową. Niepowodzenie nie było czymś, co powinna robić twoja klasa, ale teraz tak się stało. W jaki sposób rozmówca może zrobić cokolwiek, aby rozwiązać ten problem?
Jeśli może obsłużyć numer undef
, osoba dzwoniąca w rzeczywistości nie potrzebowała uchwytu pliku ... więc dlaczego poprosiła o niego obiekt?
Mając to na uwadze, jedynym rozsądnym rozwiązaniem jest śmierć. Nie możesz spełnić warunków umowy, na którą się zgodziłeś, a die
to jedyny sposób, w jaki możesz wyjść z tej sytuacji. Po prostu zrób to; śmierć jest faktem.
Teraz, jeśli nie jesteś przygotowany na śmierć, gdy budowniczy działa, musisz zmienić, gdy kod, który może zawieść, działa. Możesz to zrobić w czasie budowy obiektu, albo czyniąc go leniwym, albo jawnie ożywiając atrybut w BUILD (BUILD { $self->file_name }
).
Lepszym rozwiązaniem jest, aby nie narażać uchwyt pliku do świata zewnętrznego w ogóle, a zamiast zrobić coś takiego:
# 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;
}
Teraz wiesz, kiedy program umrze; w new
lub w write_log
. Wiesz, ponieważ doktorzy tak mówią.
Drugi sposób sprawia, że kod jest dużo czystszy; Konsument nie musi wiedzieć o implementacji twojej klasy, po prostu musi wiedzieć, że może napisać kilka komunikatów. Teraz osoba dzwoniąca nie jest zainteresowana twoimi szczegółami implementacji; po prostu mówi klasie, czego naprawdę chce.
I śmierć w write_log
może nawet być czymś, z czego można odzyskać (w bloku catch), podczas gdy "nie można otworzyć tej przypadkowej, nieprzejrzystej rzeczy, o której nie powinieneś wiedzieć", jest znacznie trudniejsze, aby osoba dzwoniąca odzyskać od.
Zasadniczo zaprojektuj swój kod w porządku, a wyjątki są jedyną odpowiedzią.
(Nie rozumiem całego "oni są kludge" tak działają w taki sam sposób w C++ i bardzo podobnie w Javie i Haskell i każdym innym języku. Czy słowo die
jest naprawdę tak przerażające czy coś?)
Dlaczego nie po prostu wyrzucić wyjątek? – friedo
@friedo, proszę wyjaśnij, jak poradzisz sobie z wyjątkami w tym przypadku. Obawiam się, że jeśli wyrzucę fatalny wyjątek w konstruktorze, oznacza to, że za każdym razem, gdy uzyskuję dostęp do 'file_handle', będę musiał być gotowy na złapanie wyjątku. – daotoad
Jeśli nie masz wyjątku, będziesz musiał sprawdzić, co powróciłeś i zrobić coś innego, jeśli się nie uda - sprawdzenie wyjątku to prawdopodobnie mniej kodu. – Mark