Am'am fost pondering această problemă pentru un timp acum și mă găsesc în permanență găsirea limitări și contradicții, așa că am'm sperând că cineva poate produce o concluzie la următoarele:
Cât de departe am'm, conștient, de lucru în industria de patru ani, citind cărți și bloguri, etc. cele mai bune practici actuale pentru manipularea erorilor este de a arunca excepții, mai degrabă decât întorcându-coduri de eroare (nu neapărat un cod de eroare, ci un tip care reprezintă o eroare).
Dar - pentru mine acest lucru pare să contrazică...
Am codul de la interfețe sau a captărilor de a reduce de cuplare. Am don't știu, sau vrei să știi, tipul specific și punerea în aplicare a unei interfețe. Deci, cum am putea ști ce excepții ar trebui să fie în căutarea de a prinde? Punerea în aplicare ar putea arunca 10 diferite excepții, sau ar putea arunca nici unul. Atunci când ne prinde o excepție, cu siguranță, ne-am're de a face presupuneri cu privire la punerea în aplicare?
Dacă nu - interfața a...
Unele limbi permite dezvoltatorilor să afirmăm că anumite metode arunca anumite excepții (Java, de exemplu, folosește de aruncari de cuvinte cheie.) Din codul de asteptare's punctul de vedere acest lucru pare bine știm în mod explicit excepții care am putea avea nevoie pentru a prinde.
Dar - asta pare să sugereze o...
De ce ar trebui să o interfata specifica excepții care pot fi aruncate? Dacă implementarea nu't nevoie pentru a arunca o excepție, sau are nevoie pentru a arunca alte excepții? Nu's nici un fel, la un nivel de interfață, pentru a ști care excepții de o punere în aplicare poate doriți să arunce.
Deci...
De ce sunt excepții de preferat atunci când acestea par (în ochii mei) să contrazică software-ul de cele mai bune practici? Și, dacă codurile de eroare sunt așa de rău (și nu't nevoie pentru a fi vândute pe vicii de coduri de eroare), există o altă alternativă? Ce este fișierul curent (sau în curând să fie) de stat de arta pentru eroare de manipulare, care îndeplinește cerințele de bune practici menționate mai sus, dar nu't se bazează pe asteptare cod verificarea întoarce valoarea de coduri de eroare?
Mai întâi de toate, eu nu sunt de acord cu această afirmație:
Favoarea excepții peste coduri de eroare
Acest lucru nu este întotdeauna cazul: de exemplu, să ia o privire la Objective-C (cu cadru de Fundație). Acolo NSError este de preferat să se ocupe de erori, în ciuda existenței a ceea ce un dezvoltator Java-ar numi adevărat excepții: @încercați, @prinde, @arunca, NSException clasa, etc.
Cu toate acestea este adevărat că multe interfețe de scurgere lor captărilor cu excepții aruncate. Convingerea mea este că acest lucru nu este vina "excepție"-stil de eroare de înmulțire/manipulare. În general, cred că cel mai bun sfat despre eroare de manipulare este aceasta:
Se ocupe de eroare/excepție la cel mai mic nivel posibil, perioada de
Cred că dacă bastoane de la această regulă de degetul mare, cantitatea de "scurgere" de la abstracții poate fi foarte limitată și controlată.
Dacă excepții aruncate printr-o metodă ar trebui să fie parte a declarației sale, cred că ar trebui să: acestea sunt o parte din contract definite prin această interfață: Această metodă face-O, sau nu reușește cu B sau C.
De exemplu, dacă o clasă este un Parser XML, o parte din designul său ar trebui să fie pentru a indica faptul că fișierul XML furnizat este pur și simplu greșit. În Java, în mod normal, nu atât prin declararea excepții te astepti de a întâlni și de a le adăuga la aruncari parte din declarația de metodă. Pe de altă parte, dacă unul dintre algoritmi de parsare nu a reușit, nu's nici un motiv pentru a trece că excepția de mai sus netratată.
Totul se reduce la un singur lucru: Bun design de interfață. Dacă design de interfață destul de bine, nici o cantitate de excepții ar trebui să te bântuie. În caz contrar, se's nu doar excepții care ar putea să te deranjeze.
De asemenea, cred că creatorii Java avut foarte puternice motive de securitate pentru a include excepții de la o metodă declararea/definirea.
Un ultim lucru: Unele limbi, Eiffel, de exemplu, au alte mecanisme pentru eroare de manipulare și pur și simplu nu includ aruncarea capacități. Acolo, o 'excepție' a fel este ridicat automat atunci când un postcondiția pentru o verificare de rutina nu este îndeplinită.
Aș dori doar să rețineți că excepțiile și codurile de eroare sunt't singura modalitate de a face cu erori și alternative căi de cod.
Din partea de sus a minții, puteți avea o abordare ca cea luată de Haskell, în cazul în care erorile pot fi semnalate prin tipurile abstracte de date cu mai mulți constructori (cred discriminate enum, sau null pointer, dar typesafe și cu posibilitatea de a adăuga syntatic zahăr sau helper funcții pentru a face fluxul de cod arata bine).
func x = do
a <- operationThatMightFail 10
b <- operationThatMightFail 20
c <- operationThatMightFail 30
return (a + b + c)
operationThatMightfail este o funcție care returnează o valoare înfășurat într-un "Poate". Acesta funcționează ca un null pointer, dar face notația garanții că totul se evaluează la null dacă orice a, b, sau c eșua. (și compilatorul protejează de a face un accident NullPointerException)
O altă posibilitate este de a trece o eroare handler obiect ca un argument in plus pentru fiecare funcție-ai sunat. Această eroare handler are o metodă pentru fiecare posibil "excepție", care pot fi semnalate prin funcția de a trece la, și poate fi utilizat de către această funcție pentru tratarea excepțiilor în cazul în care acestea apar, fără a avea neapărat pentru a derula înapoi în stivă prin excepții.
Common LISP face acest lucru, și face posibil de a avea syntatic suport (implicit argumente) și având funcții built-in urma acestui protocol.
Da, excepții pot provoca permeabil abstracții. Dar sunt coduri de eroare nu chiar mai rău în această privință?
O modalitate de a face cu această problemă este de a avea interfata specifica exact care excepțiile pot fi aruncate în ce circumstanțe și să declare că implementările trebuie să harta lor interne excepție de la acest model caietul de sarcini, prin prinderea, de conversie și de re-arunca excepții, dacă este necesar. Dacă doriți un "prefect" interfață, care's mod de a merge.
În practică, se's, de obicei, suficiente pentru a specifica excepțiile care sunt logic parte a interfeței și pe care un client poate doriți pentru a prinde și de a face ceva. L's, în general, înțelege că pot exista și alte excepții atunci când erori low-level întâmpla sau un bug manifestă, și pe care un client poate ocupa doar, în general, prin afișarea unui mesaj de eroare și/sau a închide aplicația. Cel puțin o excepție poate conține încă informații care ajută la diagnosticarea problemei.
În fapt, cu coduri de eroare, destul de mult același lucru se întâmplă doar într-o mai implicit de moda, și cu mult mai mult probabilitatea de informații obtinerea pierdut și aplicația se încheie într-o stare inconsistentă.
O mulțime de lucruri bune aici, am'd vrea să adaug că noi toți ar trebui să se abtine de cod care foloseste excepții, ca parte normală a fluxului de control. Uneori, oamenii în capcana asta în cazul în care ceva care nu't de obicei caz, devine o excepție. Am'am văzut chiar și o excepție folosit ca o buclă condiție de terminare.
Exceptii adica "ceva ce nu pot't se ocupe de aici s-a întâmplat, trebuie să mă duc la cineva să dau seama ce să fac." Un utilizator tastarea de intrare invalid e't o excepție (care ar trebui să fie gestionate la nivel local de intrare prin a cere din nou, etc.).
Un alt degenerat caz de excepție de utilizare a mi'am văzut oameni ale căror prim răspuns este "arunca o excepție." Acest lucru este aproape întotdeauna făcut, fără a scris prinde (regula de degetul mare: scrie prinde primul rând, apoi arunca declarație). În mare aplicații, acest lucru devine problematic atunci când o excepție neprins bule din nether-regiuni și explodează program.
Am'm nu anti-excepții, dar se pare ca singletons de acum câțiva ani: folosit mult prea des și nepotrivit. Ei're perfect pentru utilizarea prevăzută, dar acest caz nu este la fel de larg cum cred unii.
Permeabil abstractizare
de Ce ar trebui să o interfata specifica excepții care pot fi aruncat? Dacă implementarea nu't nevoie pentru a arunca o excepție, sau are nevoie pentru a arunca alte excepții? Nu's nici un fel, la un nivel de interfață, pentru a ști care excepții de o punere în aplicare mai vreau pentru a arunca.
Nu. Excepție caietul de sarcini sunt în aceeași găleată reveni ca și tipuri de argumente - acestea sunt o parte din interfață. Dacă poți't sunt conforme cu specificația că, atunci don't implementeze interfața. Dacă nu te arunca, apoi, ca's bine. Nu's nimic permeabil despre specificarea excepțiilor într-o interfață.
Codurile de eroare sunt dincolo de rău. Ei're teribil. Trebuie să manual amintiți-vă pentru a verifica și de a le propaga, de fiecare dată, pentru fiecare apel. Acest lucru încalcă USCAT, pentru un început, și masiv suflă în eroare-cod de tratare. Această repetiție este o problemă mult mai mare decât orice cu care se confruntă excepții. Niciodată nu poți tăcere ignora o excepție, dar oamenii pot face și în tăcere ignora coduri de returnare - cu siguranta un lucru rău.
Ei bine Excepție de manipulare poate avea propria interfață de punere în aplicare. În funcție de tipul de excepție aruncat, efectuați dorit pași.
Soluția la problema de design este de a avea două interfață/abstractizare implementări. Unul pentru funcționalitatea și celălalt pentru tratarea exceptiilor. Și în funcție de tipul de Excepție prins, apel corespunzătoare excepție de tip clasă.
Punerea în aplicare a codurilor de Eroare este ortodox mod de manipulare excepție. Este ca utilizarea șir vs șir de constructor.
IM-foarte-HO excepții sunt pentru a fi judecat de la caz la caz, pentru că de rupere a fluxului de control vor crește reale și percepute complexitatea codului, în multe cazuri, atât de inutil. Lăsând la o parte discuțiile referitoare la aruncarea excepțiilor în interiorul funcții care s - ar putea chiar îmbunătăți fluxul de control, dacă este să se uite la aruncarea excepțiilor prin apel limite, luați în considerare următoarele:
Care să permită o persoana pentru a rupe fluxul de control nu poate oferi nici un beneficiu real, și nu poate fi nici un fel semnificativ de-a face cu o excepție. Pentru o directă de exemplu, dacă unul este de punere în aplicare Observabil model (într-un limbaj cum ar fi C# în cazul în care aveți evenimente peste tot și nu explicit aruncă
în definiție), nu există nici un motiv real să Observator rupe fluxul de control dacă se blochează, și nu mod semnificativ de-a face cu lucrurile lor (desigur, un vecin bun nu ar trebui să arunce când observarea, dar nimeni nu's perfect).
Observația de mai sus poate fi extinsă la orice slab cuplate interfață (cum ai subliniat); cred că-l's, de fapt, o normă care după târâtor în sus 3-6 cadre stiva, o excepție neprins este probabil să se încheie cu o secțiune de cod care fie:
Având în vedere cele de mai sus, decorarea interfețe cu aruncari de semantica este doar un marginală a câștigului funcțional, pentru că o mulțime de vizitatori prin interfață contracte-ar pasa doar dacă nu ai reușit, nu de ce.
Aș spune că atunci devine o chestiune de gust și confort: se concentreze dvs. principal este grațios recuperarea de stat în ambele apelant și apelat după o "excepție", prin urmare, dacă aveți o mulțime de experiență în mișcare coduri de eroare în jurul valorii de (vine de la un C fundal), sau daca're de lucru într-un mediu în care excepțiile poate transforma răul (C++), nu't cred că a aruncat lucrurile în jurul valorii de este atât de important pentru frumos, curat OOP care le poate't se bazează pe modele vechi daca're inconfortabil cu ea. Mai ales dacă aceasta duce la o rupere de SoC.
Dintr-o perspectivă teoretică, cred că un SoC-kosher mod de manipulare excepții pot fi derivate direct de la observația că de cele mai multe ori direct apelantului îi pasă doar că nu a reușit, nu de ce. Callee aruncă, cineva foarte apropiat de mai sus (2-3 rame) prinde un upcasted versiune, iar excepția reală este întotdeauna scufundat la o specialitate de tratare a erorilor (chiar dacă numai contur) - acest lucru este în cazul în care AOP-ar veni la îndemână, pentru că aceste stivuitoare sunt susceptibile de a fi orizontale.
Favoarea excepții peste coduri de eroare
Atât ar trebui să coexiste.
Întoarce codul de eroare atunci când anticipa un anumit comportament.
Retur de excepție când nu anticipa un comportament.
Coduri de eroare sunt asociate în mod normal cu un singur mesaj, atunci când tipul de excepție rămâne, însă, un mesaj poate varia
Excepție are un stack trace, atunci când codul de eroare nu't. Eu nu't folosi coduri de eroare de depanare sistem defect.
de Codificare pentru interfețele nu implementări
Acest lucru ar putea fi specifice pentru JAVA, dar când îmi declar interfețe nu specifica ce excepții ar putea fi aruncate de o implementare a interfeței, se't sens.
Când ne-am prinde o excepție, cu siguranță, ne-am're face presupuneri despre punerea în aplicare?
Acest lucru este în întregime până la tine. Puteți încerca și prinde un tip foarte specific de excepție și apoi prinde-o mai general de "Excepție". De ce nu lasa excepție propaga până în stivă și apoi să-l ocupe? Alternativ, poti sa te uiti la aspect programare unde excepție de manipulare devine o "plug" aspect.
dacă implementarea nu't nevoie pentru a arunca o excepție, sau are nevoie pentru a arunca alte excepții?
Eu nu't înțeleg de ce este o problemă pentru tine. Da, este posibil să aveți o implementare care nu dă greș niciodată sau aruncă excepții și poate fi o punere în aplicare diferite, care nu în mod constant și aruncă excepție. Dacă asta'e cazul, atunci nu't specifica toate excepțiile pe interfață și problema este rezolvată.
S-ar schimba nimic dacă în loc de excepție implementarea returnat un obiect de rezultat? Acest obiect va conține rezultatul de acțiune împreună cu erori/defecțiuni dacă este cazul. Apoi, puteți interoga acel obiect.
Găsesc că excepțiile permite să scrie o mai structurat și concis cod de raportare și de manipulare erori: utilizarea codurilor de eroare, necesită verificarea reveni valorile după fiecare apel și de a decide ce să facă în caz de un rezultat neașteptat.
Pe de altă parte, sunt de acord că excepțiile dezvăluie detalii de implementare care ar trebui să fie ascuns codul invocând o interfață. Deoarece nu este posibil să se cunoască a priori care bucata de cod care poate arunca excepții (dacă nu sunt declarate în semnătura metodă ca și în Java), prin utilizarea excepții, am introdus foarte complex implicit dependențe între diferite părți ale codului, care este împotriva principiului de minimizare a acestora.
Rezumând:
Permeabil abstractizare
de Ce ar trebui să o interfata specifica excepții care pot fi aruncat? Dacă implementarea nu't nevoie pentru a arunca o excepție, sau are nevoie pentru a arunca alte excepții? Nu's nici un fel, la un nivel de interfață, pentru a ști care excepții de o punere în aplicare mai vreau pentru a arunca.
În experiența mea, codul care primește eroare (fie prin excepție, codul de eroare sau orice altceva) nu ar fi în mod normal de îngrijire pentru cauza exactă a erorii - ar reacționa în același mod pentru orice eșec, cu excepția pentru o posibilă raportare a erorii (fie că este vorba de o eroare de dialog sau un fel de jurnal); și această raportare ar fi făcut ortogonal la codul care a sunat la lipsa de procedura. De exemplu, acest cod ar putea trece de la eroare la un alt bucată de cod care știe cum să se raporteze anumite erori (de exemplu format un șir de mesaje), eventual atașarea unele informații de context.
Desigur, în unele cazuri, este necesar să se atașeze semantică specifică pentru erori și de a reacționa diferit în funcție de care a apărut o eroare. Astfel de cazuri ar trebui să fie documentate în caietul de sarcini interfață. Cu toate acestea, interfața pot rezerva dreptul de a arunca alte excepții, cu nici un sens specific.
Corect-Fie părtinitoare.
Se poate't fi ignorat, acesta trebuie să fie manipulate, l's complet transparent. Și DACĂ utilizați corect stângaci tip de eroare se transmite aceeași informație ca un java excepție.
Dezavantaj? Codul cu manipularea corespunzătoare a erorii se pare dezgustător (valabil pentru toate mecanismele).