2010-01-29 12 views
10

बिल्डर विधि में विफलता को संभालने का सबसे अच्छा तरीका क्या है?यदि मूस बिल्डर विधि विफल हो जाती है तो मुझे क्या करना चाहिए?

उदाहरण के लिए:

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 सत्य वापस आ जाएगा, भले ही मान undef हो।

वहाँ संकेत देने के लिए कि बिल्डर विफल रहा है, और विशेषता को मंजूरी दे दी रहना चाहिए कोई तरीका है?

+4

क्यों न केवल अपवाद फेंक दें? – friedo

+0

@friedo, कृपया इस मामले में अपवादों को कैसे संभालेंगे इस पर विस्तृत करें। मेरी चिंता यह है कि अगर मैं बिल्डर में घातक अपवाद फेंकता हूं, तो इसका मतलब है कि जब भी मैं 'file_handle' तक पहुंचता हूं, तो मुझे अपवाद पकड़ने के लिए तैयार रहना होगा। – daotoad

+1

यदि आपके पास कोई अपवाद नहीं है तो आपको यह जांचना होगा कि आप क्या लौटते हैं और कुछ विफल होने पर कुछ अलग करते हैं - अपवाद की जांच करना शायद कम कोड है। – Mark

उत्तर

6

"सर्वश्रेष्ठ" व्यक्तिपरक है, लेकिन आप तय करने के लिए जो अपने कोड में और अधिक समझ में आता है होगा:

  1. यदि आप जब filehandle का निर्माण करने में विफल रहता है अपने कोड में जारी रख सकते हैं (यानी यह है एक पुनर्प्राप्ति योग्य स्थिति), निर्माता को अनावश्यक वापसी करनी चाहिए और प्रकार की बाधा 'Maybe[IO::File]' पर सेट करनी चाहिए। इसका मतलब है कि जब भी आप इसका उपयोग करते हैं तो उस विशेषता पर परिभाषा की जांच भी करनी होगी। यदि यह विशेषता BUILD में ठीक से बनाया गया तुम भी जांच कर सकता है, और उस बिंदु पर आगे की कार्रवाई कर सकते हैं (के रूप में friedo उसकी टिप्पणी में संकेत), उदा clear_file_handle को कॉल करना अगर यह अनिश्चित है (चूंकि एक निर्माता हमेशा विशेषता के लिए मान निर्दिष्ट करेगा, मान लेता है कि यह निश्चित रूप से मर नहीं जाता है)।

  2. अन्यथा, बिल्डर, असफल या तो स्पष्ट एक अपवाद (जो आप उच्च ऊपर पकड़ने के लिए विकल्प चुन सकते हैं) फेंक, या बस undef लौटने और प्रकार बाधा असफल अनुमति से करते हैं। किसी भी तरह से आपका कोड मर जाएगा; आपको बस यह पसंद है कि यह कैसे मरता है और स्टैक ट्रेस कितना विशाल है। :)

पी एस। तुम भी Try::Tiny को देखने के लिए है, जो मूस आंतरिक रूप से उपयोग करता है, और मूल रूप से सिर्फ * do eval { blah } or die ... मुहावरा के लिए एक आवरण है सकते हैं।

* लेकिन ठीक हो गया! और एक शांत तरीके से! (मैं #moose से मेरे कान में फुसफुसाते के बहुत सारे सुनने के लिए लग रहे हैं ..)

+0

मैंने कोशिश :: टिनी पर देखा है। मैं किसी अपवाद हैंडलिंग मॉड्यूल पर कूदने में संकोच कर रहा हूं, क्योंकि मुझे पिछले "अद्भुत नए" वर्ग बिल्डरों और अपवाद हैंडलर का उपयोग करके बुरी तरह जला दिया गया है। मूस को मौका देने में मुझे काफी समय लगा। – daotoad

+2

मुझे समझ में नहीं आता; कोशिश करें :: टिनी वास्तव में तुच्छ है और पर्ल में लगभग एक बग है जो व्यवहार के आसपास काम करने के लिए आवश्यक कुछ भी नहीं करता है। मैं मूस के रूप में के रूप में दूरगामी कुछ करने के लिए प्रतिबद्ध करने में संकोच किया जा रहा हो, लेकिन आप लगभग 5 मिनट में के छोटे स्रोत पढ़ने और समझने की कोशिश कर सकते ::। आपको जलाने के लिए क्या है? – hdp

+1

ईथर, धन्यवाद। मैंने ट्राई :: टिनी को देखने के लिए समय निकाला, और यह एक बल्कि पूरी तरह से स्नीफ परीक्षण पारित किया। जब कोड ठीक से संरचित किया जाता है, तो 'try {} 'ब्लॉक की संख्या को कम करना मुश्किल नहीं होता है। अब तक, मैं कोशिश :: छोटे से बहुत खुश हूँ। – daotoad

2

वहाँ संकेत देने के लिए कि बिल्डर विफल रहा है, और विशेषता को मंजूरी दे दी रहना चाहिए कोई तरीका है?

नहीं, इस मतलब नहीं होगा, बिल्डर यदि विशेषता cleaered है सक्रिय होगा, अगर यह निर्माता के अंतर्गत मुक्त कर दिया गया यह सिर्फ आग जब आप इसे करने के लिए अगले फोन किया होगा, और मंजूरी दे दी में रहते हैं राज्य। एक बहुत काम का एक बेकार, बस सेट कुछ-अगर यह काम करता है और अगर-नहीं-जारी रखने के करने के लिए।

type-union सुझाव एक अच्छा है लेकिन फिर आपको कोड लिखना होगा जो दो मूल रूप से अलग-अलग मामलों के साथ काम कर सकता है: एक फ़ाइल हैंडल, और एक गैर-मौजूद फ़ाइल संभाल। यह एक गरीब विचार की तरह लगता है।

फ़ाइल हैंडल कार्य तो यह शायद वस्तु का उपयोग करने के साथ चीजों में से एक ही दायरे के बीच साझा नहीं कर रहा है के लिए आवश्यक नहीं है। यदि यह मामला है तो ऑब्जेक्ट केवल एक विधि प्रदान कर सकता है जो ऑब्जेक्ट से फ़ाइलहेडल उत्पन्न करता है।मैं इसे उत्पादन कोड में करता हूं। सबकुछ आलसी-विशेषता बनाने पर नरक न करें, कुछ चीजें विशेषता के कार्य हैं और यह हमेशा वस्तु को संलग्न करने के लिए समझ में नहीं आता है।

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; 

}; 
9

आप उच्च स्तर पर नहीं सोच रहे हैं। ठीक है, निर्माता विफल रहता है। विशेषता अपरिभाषित बनी हुई है। लेकिन एक्सेसर को कॉल करने वाले कोड के बारे में आप क्या करते हैं? वर्ग के अनुबंध ने इंगित किया कि विधि को कॉल करना हमेशा आईओ :: फाइल लौटाएगा। लेकिन अब यह वापस आ रहा है। (अनुबंध IO::File, नहीं Maybe[IO::File], सही था?)

तो कोड की अगली पंक्ति पर, फोन करने वाले मरने के लिए ("the_caller.pl लाइन 42 पर एक अपरिभाषित मूल्य पर विधि 'ReadLine' कॉल नहीं कर सकता जा रहा है "), क्योंकि यह अपेक्षा करता है कि आपकी कक्षा उस अनुबंध का पालन करे जो इसे परिभाषित करती है। विफलता कुछ ऐसा नहीं था जो आपकी कक्षा को करना था, लेकिन अब यह हुआ। कॉलर इस समस्या को ठीक करने के लिए कुछ भी कैसे कर सकता है?

यदि यह undef को संभाल सकता है, तो कॉलर को वास्तव में फ़ाइलहेडल की शुरुआत करने की आवश्यकता नहीं थी ... तो यह आपके ऑब्जेक्ट को एक के लिए क्यों पूछता था?

इस बात को ध्यान में रखते हुए, केवल सायन समाधान मरना है। आप जिस अनुबंध से सहमत हैं, उससे आप नहीं मिल सकते हैं, और die एकमात्र तरीका है जिससे आप उस स्थिति से बाहर निकल सकते हैं। तो बस यह करो; मृत्यु जीवन का एक तथ्य है।

अब, यदि आप निर्माता बनने पर मरने के लिए तैयार नहीं हैं, तो कोड को विफल करने के दौरान आपको बदलना होगा। आप ऑब्जेक्ट-निर्माण समय पर इसे या तो आलसी बनाकर, या स्पष्ट रूप से 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 में मरना कुछ भी हो सकता है जो आप (कैच ब्लॉक में) से पुनर्प्राप्त कर सकते हैं, जबकि "इस यादृच्छिक अपारदर्शी चीज़ को नहीं खोल सका जिसे आपको किसी भी तरह से नहीं पता होना चाहिए" कॉलर को पुनर्प्राप्त करने के लिए बहुत कठिन है से।

असल में, अपने कोड को स्वच्छ रूप से डिज़ाइन करें, और अपवाद ही एकमात्र उत्तर हैं।

(मुझे पूरा नहीं मिलता है "वे एक क्लेज हैं" वैसे भी। वे सी ++ में समान तरीके से काम करते हैं और जावा और हास्केल और इसी तरह की दूसरी भाषा में भी इसी तरह काम करते हैं। क्या die शब्द वास्तव में डरावना या कुछ है?)

+0

आप और अन्य अपवादों के लिए दृढ़ता से बहस करते हैं। मैं ** तथ्य ** के कारण उनका उपयोग करने से बचने की उम्मीद कर रहा था कि वे टूट गए हैं। अपवाद फेंकना आसान है और अच्छी तरह से काम करता है। उन्हें पकड़ना भयानक है। ब्लॉक 'eval' टूटा हुआ है। मैंने पहले से ही सिस्टिन चैपल पेपर के लिए पर्याप्त eval बॉयलरप्लेट लिखा है। – daotoad

+3

कोशिश करें :: टिनी, जिसे कई धागे में कई बार सुझाव दिया गया है। पर्ल बदल नहीं जा रहा है क्योंकि आप स्टैक ओवरफ्लो पर पुस्तकालयों से डरते हैं। तो या तो एक पुस्तकालय का उपयोग करें, बॉयलरप्लेट लिखें, खुद को ठीक करें, या चले जाओ। – jrockway

+0

विभिन्न अंदरूनी तकनीकों (याद रखें कि फ़ैड) और त्रुटि.pm और अन्य कोशिश/पकड़ मॉड्यूल को आजमाकर जला दिया गया, मैंने अपनी नाक बदल दी और कोशिश की कि ट्री :: जिस मौके पर वह योग्य नहीं है। अपवादों के साथ-साथ अन्य लोगों के लिए आपके तर्क ने मुझे निष्कर्ष निकाला कि वे सही डिजाइन थे। उस के लिए, मुझे आपको धन्यवाद देना है। – daotoad

संबंधित मुद्दे

 संबंधित मुद्दे