樂(lè)在其中

          以JEE為主攻,以Flex為點(diǎn)綴,以Eclipse RCP為樂(lè)趣
          請(qǐng)?jiān)L問(wèn)http://www.inframesh.org

          首頁(yè) 新隨筆 聯(lián)系 管理
            43 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

          1 基本信息

          摘要:本文倡導(dǎo)一種對(duì)異常條件本質(zhì)的思考方式,并描述一些有助于設(shè)計(jì)的模式。最后,本文還將在AOP模型中,作為相互滲透的問(wèn)題,來(lái)討論異常的處理。當(dāng)你能正確使用異常時(shí),它們會(huì)有極大的好處。本文將幫助你做到這一點(diǎn)。

          原作者:Barry Ruzek   譯者: 易曉斕,原文:http://www.yeeyan.com/articles/view/2091/976

          2 為何異常是如此重要

            Java應(yīng)用中的異常處理在很大程度上揭示了其所基于架構(gòu)的強(qiáng)度。架構(gòu)是在應(yīng)用程序各個(gè)層次上所做出并遵循的決定。其中最重要的一個(gè)就是決定應(yīng)用程序中的類(lèi),亞系統(tǒng),或?qū)又g溝通的方式。Java異常是Java方法將另類(lèi)執(zhí)行結(jié)果交流出去的方式,所以值得在應(yīng)用架構(gòu)中給予特殊關(guān)注。

            一個(gè)衡量Java設(shè)計(jì)師水平和開(kāi)發(fā)團(tuán)隊(duì)紀(jì)律性的好方法就是讀讀他們應(yīng)用程序里的異常處理代碼。首先要注意的是有多少代碼用于捕獲異常,寫(xiě)進(jìn)日志文件,決定發(fā)生了什么,和在不同的異常間跳轉(zhuǎn)。干凈,簡(jiǎn)捷,關(guān)聯(lián)性強(qiáng)的異常處理通常表明開(kāi)發(fā)團(tuán)隊(duì)有著穩(wěn)定的使用Java異常的方式。當(dāng)異常處理代碼的數(shù)量甚至要超過(guò)其他代碼時(shí),你可以看出團(tuán)隊(duì)之間的交流合作有很大的問(wèn)題(可能在一開(kāi)始就不存在),每個(gè)人都在用他們自己的方式來(lái)處理異常。

            對(duì)突發(fā)異常的處理結(jié)果是可以預(yù)見(jiàn)的。如果你問(wèn)問(wèn)團(tuán)隊(duì)成員為什么異常會(huì)被拋出,捕獲,或在特定的一處代碼里忽視了異常的發(fā)生,他們的回答通常是,“我沒(méi)有別的可做”。如果你問(wèn)當(dāng)他們編寫(xiě)的異常真的發(fā)生了會(huì)怎么樣,他們會(huì)皺皺眉,你得到的回答類(lèi)似于這樣,“我不知道。我們從沒(méi)測(cè)試過(guò)。”

            你可以從客戶(hù)端的代碼判斷一個(gè)java的組件是否有效利用了java的異常。如果它們包含著大堆的邏輯去弄清楚在何時(shí)一筆操作失敗了,為何失敗,是否有彌補(bǔ)的余地,那么原因很有可能要?dú)w咎于組件的報(bào)錯(cuò)設(shè)計(jì)。錯(cuò)誤的報(bào)錯(cuò)系統(tǒng)會(huì)在客戶(hù)端產(chǎn)生大量的“記錄然后忘掉”的代碼,這些代碼鮮有用途。最差的是弄擰的邏輯,嵌套的try/catch/finally代碼塊,和一些其他的混亂而導(dǎo)致脆弱而難于管理的應(yīng)用程序。

            事后再來(lái)解決Java異常的問(wèn)題,或根本就不解決,是軟件項(xiàng)目產(chǎn)生混亂并導(dǎo)致滯后的主要原因。異常處理是一個(gè)在設(shè)計(jì)的各個(gè)部分都急需解決的問(wèn)題。對(duì)異常處理建立一個(gè)架構(gòu)性的約定是項(xiàng)目中首要做出的決定。合理使用Java異常模型對(duì)確保你的應(yīng)用簡(jiǎn)單,易維護(hù),和正確有著長(zhǎng)遠(yuǎn)的影響。

          3 解析異常

            正確使用Java異常模型所包含的內(nèi)容一直以來(lái)有著很大的爭(zhēng)議。Java不是第一種支持異常算法語(yǔ)義的;但是,它卻是第一種通過(guò)編譯器來(lái)執(zhí)行聲明和處理某些異常的規(guī)則的語(yǔ)言。許多人都認(rèn)為編譯時(shí)的異常檢查對(duì)精確的軟件設(shè)計(jì)頗有幫助。圖1顯示的Java異常的等級(jí)。
           

          圖1:Java異常的等級(jí)

            通常,Java編譯器強(qiáng)迫拋出基于java.lang.Throwable的異常的方法要在它聲明中的“throws”部分加上那個(gè)異常。而且,編譯器還會(huì)證實(shí)客戶(hù)端的方法或者捕獲已聲明的異常,或者特別聲明自己也拋出同樣的異常。這些簡(jiǎn)單的規(guī)則對(duì)世界范圍的Java程序員都有深遠(yuǎn)的影響。

            編譯器放松了對(duì)Throwable繼承樹(shù)中兩個(gè)分支的異常檢查。java.long.Error和java.lang.RuntimeException 的子類(lèi)免于編譯時(shí)的檢查。在這兩類(lèi)中,軟件工程師通常對(duì)運(yùn)行中異常更感興趣。“不檢查”的異常指的是這一組,以便和所有其它“檢查”的異常區(qū)別開(kāi)。

            我可以想象那些接受“檢查”的異常的人,也會(huì)很看重Java的數(shù)據(jù)類(lèi)型。畢竟,編譯器對(duì)數(shù)據(jù)類(lèi)型施加的限制鼓勵(lì)嚴(yán)格的編碼和精確的思維。編譯時(shí)的類(lèi)型檢查對(duì)減少運(yùn)行時(shí)的嚴(yán)重問(wèn)題有幫助。編譯時(shí)的異常檢查也能起到類(lèi)似的作用,它會(huì)提醒開(kāi)發(fā)人員某個(gè)方法可能會(huì)有預(yù)想不到的結(jié)果需要處理好。

            早期的建議是盡可能的使用“檢察的異常”,以此來(lái)最大限度的利用編譯器提供的幫助來(lái)寫(xiě)出無(wú)錯(cuò)誤的軟件。Java類(lèi)庫(kù)API的設(shè)計(jì)者們都認(rèn)同這一點(diǎn),他們廣泛地使用“檢察的異常”來(lái)模擬類(lèi)庫(kù)方法中幾乎所有的緊急應(yīng)變措施。在J2SE5.1 API規(guī)格中,“檢察的異常”類(lèi)型已2比1的比率超過(guò)了“未檢查的異常”類(lèi)型。

            對(duì)程序員而言,看上去在Java類(lèi)庫(kù)中大多數(shù)的常用方法對(duì)每一個(gè)可能的失敗都聲明了“檢察的異常”。例如,java.io包
          對(duì)IOException這個(gè)“檢察的異常”就有著很大的依賴(lài)。至少有63個(gè)Java類(lèi)庫(kù)包,或直接,或通過(guò)十幾個(gè)下面的子類(lèi),拋出這個(gè)異常。

            I/O的失敗極其稀有,但是卻很?chē)?yán)重。而且,一旦發(fā)生,從你所寫(xiě)的代碼里基本上是無(wú)法補(bǔ)救的。Java程序員意識(shí)到他們不得不提供IOException 或類(lèi)似的不可補(bǔ)救的事件,而一個(gè)簡(jiǎn)單的Java類(lèi)庫(kù)方法的調(diào)用就可能讓這些事件發(fā)生。捕獲這些異常給本來(lái)簡(jiǎn)單的代碼帶來(lái)了一定的晦澀,因?yàn)榧词乖诓东@的代碼塊里也基本上幫不上忙。但是不加以捕獲又可能更糟糕,因?yàn)榫幾g器要求你的方法必須要拋出那些異常。這樣你的實(shí)施細(xì)則就不得不暴露在外了,而通常好的面向?qū)ο蟮脑O(shè)計(jì)都是要隱藏細(xì)節(jié)的。

            這樣一個(gè)不可能贏的局面導(dǎo)致了我們今天所警告的絕大多數(shù)臭名卓著的異常處理的顛覆性格局。同時(shí)也衍生了很多正確或錯(cuò)誤的補(bǔ)救之道。

            一些Java界的知名人物開(kāi)始質(zhì)疑Java的“檢察的異常”的模型是否是一個(gè)失敗的試驗(yàn)。有一些東西肯定是失敗的,但是這和在Java語(yǔ)言里加入對(duì)異常的檢查是毫無(wú)關(guān)聯(lián)的。失敗是由于在Java API的設(shè)計(jì)者們的思維里,大多數(shù)失敗的情形是雷同的,所以可以通過(guò)同一種異常傳達(dá)出去。

          4 故障和應(yīng)變

            讓我們來(lái)考慮在一個(gè)假想的銀行應(yīng)用中的CheckingAccount類(lèi)。一個(gè)CheckingAcccount屬于一個(gè)用戶(hù),記載著用戶(hù)的存款余額,也能接受存款,接受止兌的通知,和處理匯入的支票。一個(gè)CheckingAcccount對(duì)象必須協(xié)調(diào)同步線(xiàn)程的訪(fǎng)問(wèn),因?yàn)槿魏我粋€(gè)線(xiàn)程都可能改變它的狀態(tài)。CheckingAcccount類(lèi)里processCheck的方法會(huì)接受一個(gè)Check對(duì)象為參數(shù),通常從帳戶(hù)余額里減去支票的金額。但是一個(gè)管理支票清算的用戶(hù)端程序調(diào)用processCheck方法時(shí),必須有兩種可能的應(yīng)變措施。一,CheckingAccount對(duì)象里可能對(duì)該支票已有一個(gè)止付的命令;二,帳戶(hù)的余額可能不足已滿(mǎn)足支票的金額。

            所以,processCheck的方法對(duì)來(lái)自客戶(hù)端的調(diào)用可以有3種方式回應(yīng)。正常的是處理好支票,并把方法簽名里聲明的結(jié)果返回給調(diào)用方。兩種應(yīng)變的回應(yīng)則是需要與支票清算端溝通的在銀行領(lǐng)域?qū)崒?shí)在在存在的情況。processCheck方法所有3種返回結(jié)果都是按照典型的銀行支票帳戶(hù)的行為而精心設(shè)計(jì)的。

            在Java里,一個(gè)自然的方法來(lái)表示上述緊急的應(yīng)變是定義兩種異常,比如StopPaymentException(止付異常)和 InsufficientFundsException(余額不足異常)。一個(gè)客戶(hù)端如果忽略這些異常是不對(duì)的,因?yàn)檫@些異常在正常操作的情況下一定會(huì)被拋出。他們?nèi)缤椒ǖ暮灻粯臃从沉朔椒ǖ娜嫘袨椤?/font>

            客戶(hù)端可以很容易的處理好這兩種異常。如果對(duì)支票的兌付被停止了,客戶(hù)端把該支票交付特別處理。如果是因?yàn)橘Y金不足,用戶(hù)端可以從用戶(hù)的儲(chǔ)蓄帳戶(hù)里轉(zhuǎn)移一些資金到支票帳戶(hù)里,然后再試一次。

            在使用CheckingAccount的API時(shí),這些應(yīng)變都是可以預(yù)計(jì)的和自然的結(jié)果。他們并不是意味著軟件或運(yùn)行環(huán)境的失敗。這些異常和由于CheckingAccount類(lèi)中一些內(nèi)部實(shí)施細(xì)則引起的真正失敗是不同的。

            設(shè)想CheckingAccount對(duì)象在數(shù)據(jù)庫(kù)里保持著一個(gè)恒定的狀態(tài),并使用JDBC API來(lái)對(duì)之訪(fǎng)問(wèn)。在那個(gè)API里,幾乎所有的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)方法都有可能因?yàn)楹虲heckingAccount實(shí)施無(wú)關(guān)的原因而失敗。比如,有人可能忘了把數(shù)據(jù)庫(kù)服務(wù)器運(yùn)行起來(lái),一個(gè)未有連上的網(wǎng)絡(luò)數(shù)據(jù)線(xiàn),訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的密碼改變了,等等。

            JDBC依靠一種“檢查的異常”,SQLException,來(lái)匯報(bào)任何可能的錯(cuò)誤。可能出錯(cuò)的絕大多數(shù)原由都是數(shù)據(jù)庫(kù)的配置,連接,或其所在的硬件設(shè)施。對(duì)processCheck方法而言,它對(duì)上述錯(cuò)誤是無(wú)計(jì)可施的。這不應(yīng)該,因?yàn)閜rocessCheck至少了解它自己的實(shí)施細(xì)則。在調(diào)用棧里上游的方法能處理這些問(wèn)題的可能就更小。

            CheckingAccount這個(gè)例子說(shuō)明了一個(gè)方法不能成功返回它想要的結(jié)果的兩個(gè)基本原因。這里是兩個(gè)描述性的術(shù)語(yǔ):

          應(yīng)變
              與實(shí)際預(yù)料相符,一個(gè)方法給出另外一種回應(yīng),而這種回應(yīng)可以表達(dá)成該方法所要達(dá)到的目的之一。這個(gè)方法的調(diào)用者預(yù)料到這個(gè)情況的出現(xiàn),并有相對(duì)的應(yīng)付之道。

          故障
              在未經(jīng)計(jì)劃的情況下,一個(gè)方法不能達(dá)到它的初衷,這是一個(gè)不訴諸該方法的實(shí)施細(xì)則就很難搞清的情況。
              應(yīng)用這些術(shù)語(yǔ),對(duì)processCheck方法而言,一個(gè)止付的命令和一個(gè)超額的提取是兩種可能的應(yīng)變。而SQLException反映了可能的故障。processCheck方法的調(diào)用者應(yīng)該能夠提供應(yīng)變,但卻不一定能有效的處理好可能發(fā)生的故障。

          Java異常的匹配
              在建立應(yīng)用架構(gòu)中Java異常的規(guī)則時(shí),以應(yīng)變和故障的方式仔細(xì)考慮好“什么可能會(huì)出錯(cuò)”是有長(zhǎng)遠(yuǎn)意義的。

            應(yīng)變情況恰如其分地匹配給了Java檢查的異常。因?yàn)樗鼈兪欠椒ǖ恼Z(yǔ)義算法合同中不可缺少的一部分,在這里借助于編譯器的幫助來(lái)確保它們得到解決是很有道理的。如果你發(fā)現(xiàn)編譯器堅(jiān)持應(yīng)變的異常必須要處理或者在不方便的時(shí)候必須要聲明會(huì)給你帶來(lái)些麻煩,你在設(shè)計(jì)上幾乎肯定要做些重構(gòu)了。這其實(shí)是件好事。

            出現(xiàn)故障的情況對(duì)開(kāi)發(fā)人員而言是蠻有意思的,但對(duì)軟件邏輯而言卻并非如此。那些軟件”消化問(wèn)題“的專(zhuān)家們需要關(guān)于故障的信息以便來(lái)解決問(wèn)題。因此,未檢查的異常是表示故障的很好方式。他們讓故障的通知原封不動(dòng)地從調(diào)用棧上所有的方法濾過(guò),到達(dá)一個(gè)專(zhuān)門(mén)來(lái)捕獲它們的地方,并得到它們自身包含的有利于診斷的信息,對(duì)整個(gè)事件提供一個(gè)有節(jié)制的優(yōu)雅的結(jié)論。產(chǎn)生故障的方法不需要來(lái)聲明(異常),上游的調(diào)用方法不需要捕獲它們,方法的實(shí)施細(xì)則被正確的隱藏起來(lái)- 以最低的代碼復(fù)雜度。

            新一些的Java API,比如像Spring架構(gòu)和Java Data Ojects類(lèi)庫(kù)對(duì)檢查的異常幾乎沒(méi)有依賴(lài)。Hibernate ORM架構(gòu)在3.0版本里重新定義了一些關(guān)鍵功能來(lái)去除對(duì)檢查的異常的使用。這就意味著在這些架構(gòu)舉報(bào)的絕大部分異常都是不可恢復(fù)的,歸咎于錯(cuò)誤的方法調(diào)用代碼,或是類(lèi)似于數(shù)據(jù)庫(kù)服務(wù)器之類(lèi)的底層部件的失敗。特別的,強(qiáng)迫一個(gè)調(diào)用方來(lái)捕獲或聲明這些異常幾乎沒(méi)有任何好處。
          設(shè)計(jì)里的故障處理

            在你的計(jì)劃里,承認(rèn)你需要去做就邁好了有效處理好故障的第一步。對(duì)那些堅(jiān)信自己能寫(xiě)出無(wú)懈可擊的軟件的工程師們來(lái)說(shuō),承認(rèn)這一點(diǎn)是不容易的。這里是一些有幫助的思考方式。首先,如果錯(cuò)誤俯拾即是,應(yīng)用的開(kāi)發(fā)時(shí)間將很長(zhǎng),當(dāng)然前提是程序員自己的bug自己修理。第二,在Java類(lèi)庫(kù)中,過(guò)度使用檢查的異常來(lái)處理故障情形將迫使你的代碼要應(yīng)對(duì)好故障,即使你的調(diào)用次序完全正確。如果沒(méi)有一個(gè)故障處理的架構(gòu),湊合的異常處理將導(dǎo)致應(yīng)用中的信息丟失。

          一個(gè)成功的故障處理架構(gòu)一定要達(dá)到下面的目標(biāo):
          • 減少代碼的復(fù)雜性
          • 捕獲和保存診斷性信息
          • 對(duì)合適的人提醒注意
          • 優(yōu)雅地退出行動(dòng)

            故障是應(yīng)用的真實(shí)意圖的干擾。因此,用來(lái)處理它們的代碼應(yīng)盡量的少,理想上,把它們和應(yīng)用的語(yǔ)義算法部分隔離開(kāi)。故障的處理必須滿(mǎn)足那些負(fù)責(zé)改正它們的人的需要。開(kāi)發(fā)人員需要知道故障發(fā)生了,并得到能幫助他們搞清為何發(fā)生的信息。即使一個(gè)故障,在定義上而言,是不可補(bǔ)救的,好的故障處理會(huì)試著優(yōu)雅地結(jié)束引起故障的活動(dòng)。

          對(duì)故障情況使用未檢查的異常

            在做框架上的決定時(shí),用未檢查的異常來(lái)代表故障情況是有很多原因的。Java的運(yùn)行環(huán)境對(duì)代碼的錯(cuò)誤會(huì)拋出“運(yùn)行時(shí)異常”的子類(lèi),比如, ArithmeticException或ClassCastException。這為你的框架設(shè)了一個(gè)先例。未檢查的異常讓上游的調(diào)用方法不需要為和它們目的不相關(guān)的情況而添加代碼,從而減少了混亂。

            你的故障處理策略應(yīng)該認(rèn)識(shí)到Java類(lèi)庫(kù)的方法和其他API可能會(huì)使用檢查的異常來(lái)代表對(duì)你的應(yīng)用而言只可能是故障的情況。在這種情形下,采用設(shè)計(jì)約定來(lái)捕獲API異常,將其以故障來(lái)看待,拋出一個(gè)未檢查的異常來(lái)指示故障的情況和捕獲診斷的信息。

            在這種情況下拋出的特定異常類(lèi)型應(yīng)該由你的框架來(lái)定義。不要忘記一個(gè)故障異常的主要目的是傳遞記錄下來(lái)的診斷信息,以便讓人們來(lái)想出出錯(cuò)的原因。使用多個(gè)故障異常類(lèi)型可能有些過(guò),因?yàn)槟愕募軜?gòu)對(duì)它們都一視同仁。多數(shù)情況下,一條好的,描述性強(qiáng)的信息將單一的故障類(lèi)型嵌入就夠用了。使用Java基本的 RuntimeException來(lái)代表故障情況是很容易的。截止到Java1.4,RuntimeException,和其他的拋出類(lèi)型一樣,都支持異常的嵌套,這樣你就可以捕獲和報(bào)出導(dǎo)向故障的檢查的異常。

            你也許會(huì)為了故障報(bào)告的目的而定義你自己的未檢查的異常。這樣做可能是必要的,如果你使用Java1.3或更早的版本,它們都不支持異常的嵌套。實(shí)施一個(gè)類(lèi)似的嵌套功能來(lái)捕獲和轉(zhuǎn)換你應(yīng)用中構(gòu)成故障的檢查的異常是很簡(jiǎn)單的。你的應(yīng)用在報(bào)錯(cuò)時(shí)可能需要一個(gè)特殊的行為。這可能是你在架構(gòu)中創(chuàng)建 RuntimeException子類(lèi)的另一個(gè)原因。

          建立一個(gè)故障的屏障

            對(duì)你的故障處理架構(gòu)而言,決定拋出什么樣的異常,何時(shí)拋出是重要的決定。同樣重要的是,何時(shí)來(lái)捕獲一個(gè)故障異常,之后再怎么辦。這里的目的是讓你應(yīng)用中的功能性部分不需要處理故障。把問(wèn)題分開(kāi)來(lái)處理通常都是一件好事情,有一個(gè)中央故障處理機(jī)制長(zhǎng)遠(yuǎn)來(lái)看是很有裨益的。

            在故障屏障的模式里,任何應(yīng)用組件都可以?huà)伋龉收袭惓#侵挥凶鳛?#8220;故障屏障”的組件才捕獲異常。采用此種模式去除了大多數(shù)程序員為了在本地處理故障而插入的復(fù)雜的代碼。故障屏障邏輯上位于調(diào)用棧的上層,這樣在一個(gè)默認(rèn)的行動(dòng)被激發(fā)前,一個(gè)異常向上舉報(bào)的行為就被阻止了。根據(jù)不同的應(yīng)用類(lèi)型,默認(rèn)的行動(dòng)所指也不同。對(duì)一個(gè)獨(dú)立的Java應(yīng)用而言,這個(gè)行動(dòng)指活著的線(xiàn)程被停止。對(duì)一個(gè)位于應(yīng)用服務(wù)器上的Web應(yīng)用而言,這個(gè)行動(dòng)指應(yīng)用服務(wù)器向?yàn)g覽器送出不友好的(甚至令人尷尬的)回應(yīng)。

            一個(gè)故障屏障組件的第一要?jiǎng)?wù)就是記錄下故障異常中包含的信息以為將來(lái)所用。到現(xiàn)在為止,一個(gè)應(yīng)用日志是做成此事的首選。異常的嵌套的信息,棧日志,等等,都是對(duì)診斷有價(jià)值的信息。傳遞故障信息最差的地方是通過(guò)用戶(hù)界面。把應(yīng)用的使用者卷進(jìn)查錯(cuò)的進(jìn)程對(duì)你,對(duì)你的用戶(hù)而言都不好。如果你真的很想把診斷信息放上用戶(hù)界面,那可能意味著你的日志策略需要改進(jìn)。

            故障屏障的下一個(gè)要?jiǎng)?wù)是以一種可控的方式來(lái)結(jié)束操作。這具體的意義要取決于你應(yīng)用的設(shè)計(jì),但通常包括產(chǎn)生一個(gè)可通用的回應(yīng)給可能正在等待的客戶(hù)端。如果你的應(yīng)用是一個(gè)Web service,這就意味著在回應(yīng)中用soap:Server的<faultcode>和通用的失敗信息< faultstring>來(lái)建立一個(gè)SOAP故障元素<fault>。如果你的應(yīng)用于瀏覽器交流,這個(gè)屏障就會(huì)安排好一個(gè)通用的 HTML回應(yīng)來(lái)表明需求是不能被處理的。

            在一個(gè)Struts的應(yīng)用里,你的故障屏障會(huì)以一種全局異常處理器的形式出現(xiàn),并被配置成處理RuntimeException的任何子類(lèi)。你的故障屏障類(lèi)將延伸org.apache.struts.action.ExceptionHandler類(lèi),必要的話(huà),重寫(xiě)它的方法來(lái)實(shí)施用戶(hù)自己的特別處理。這樣就會(huì)處理好不小心產(chǎn)生的故障情況和在處理一個(gè)Struts動(dòng)作時(shí)發(fā)現(xiàn)的故障。圖2顯示的就是應(yīng)變和故障異常。
           

          圖2 應(yīng)變和故障異常

            如果你使用的是Spring MVC架構(gòu),你可以繼承SimpleMappingExceptionResolver類(lèi),并配置成處理RuntimeException和它的子類(lèi)們,這樣很容易的就建起了故障屏障。通過(guò)重寫(xiě)resolveException的方法,你可以在使用父類(lèi)的方法來(lái)把需求導(dǎo)引到一個(gè)發(fā)出通用錯(cuò)誤提示的view 組件之前,加入你需要的用戶(hù)化的處理。

            當(dāng)你的架構(gòu)包含了故障屏障,程序員都知曉了后,再寫(xiě)出一次性的故障異常的沖動(dòng)就會(huì)銳減。結(jié)果就是應(yīng)用中出現(xiàn)更干凈,更易于維護(hù)的代碼。

          5 架構(gòu)中應(yīng)變的處理

            將故障處理交與屏障后,主要組件間的應(yīng)變交流變得容易多了。一個(gè)應(yīng)變代表著與主要返回結(jié)果同等重要的另外一種方法結(jié)果。因此,檢查的異常類(lèi)型是一個(gè)能夠很好地傳遞應(yīng)變情況的存在并提供必要的信息來(lái)與它競(jìng)爭(zhēng)的工具。這個(gè)方式借助于Java編譯器的幫助來(lái)提醒程序員關(guān)于他們所用的API的方方面面以及提供全套的方法輸出的必要性。

            僅僅使用方法的返回值類(lèi)型來(lái)傳遞簡(jiǎn)單的應(yīng)變是可能的。比如,返回一個(gè)空引用,而不是一個(gè)具體的對(duì)象,可以意味著對(duì)象由于一個(gè)已定義的原因不能被建立。 Java I/O的方法通常返回一個(gè)整數(shù)值-1,而不是字節(jié)的值或字節(jié)的數(shù)來(lái)表示文件的結(jié)尾。如果你的方法的語(yǔ)義簡(jiǎn)單到可以允許的地步,另一種返回值的方法是可以使用的,因?yàn)樗饤壛水惓?lái)的額外的花銷(xiāo)。不足之處是方法的調(diào)用方要檢測(cè)一下返回的值來(lái)判斷是主要結(jié)果,還是應(yīng)變結(jié)果。但是,編譯器沒(méi)有辦法來(lái)保證方法調(diào)用者會(huì)使用這個(gè)判斷。

            如果一個(gè)方法有一個(gè)void的返回類(lèi)型,異常是唯一的方法來(lái)表示應(yīng)變發(fā)生了。如果一個(gè)方法返回的是一個(gè)對(duì)象的引用,那么返回值只可能是空或非空(null and non-null)。如果一個(gè)方法返回一個(gè)整數(shù)型,選擇與主要返回值不沖突的,可以表示多種應(yīng)變情況的數(shù)值是可能的。但是這樣的話(huà),我們就進(jìn)入了錯(cuò)誤代碼檢查的世界,而這正式Java異常模式所著力避免的。
          提供一些有用的信息

            定義不同的故障報(bào)告的異常類(lèi)型是沒(méi)什么道理的,因?yàn)楣收掀琳蠈?duì)所有異常類(lèi)型一視同仁。應(yīng)變異常就有很大的不同,因?yàn)樗鼈兊脑馐且蚍椒ㄕ{(diào)用者傳遞各種情況。你的架構(gòu)可能會(huì)指出這些異常應(yīng)該繼承java.lang.Exception或一個(gè)指定的基類(lèi)。

            不要忘記你的異常應(yīng)該是百分百的Java類(lèi)型,你可以用它來(lái)存放為你的特殊目的服務(wù)的特殊字段,方法,甚至是構(gòu)造器。比如,被假想的 processCheck()方法拋出的InsufficientFundsException這個(gè)異常類(lèi)型就應(yīng)該包含著一個(gè) OverdraftProtection的對(duì)象,它能夠從另外一個(gè)帳戶(hù)里把短缺的資金轉(zhuǎn)過(guò)來(lái)。

          日志還是不要日志

            記錄下故障異常是有用處的,因?yàn)槿罩镜哪康氖窃谝恍┬枰恼那闆r下,日志可以吸引人們的注意力。但對(duì)應(yīng)變異常而言卻并非如此。應(yīng)變異常可能代表的只是極少數(shù)情況,但是在你的應(yīng)用里,每一個(gè)情況還是會(huì)發(fā)生的。它們意味著你的應(yīng)用正在如最初的設(shè)計(jì)般正常工作著。經(jīng)常把日志代碼加進(jìn)應(yīng)變的捕獲塊里會(huì)使你的代碼晦澀難懂,而又沒(méi)有實(shí)際的好處。如果一個(gè)應(yīng)變代表了一重要的事件,在拋出一個(gè)異常應(yīng)變來(lái)警醒調(diào)用者之前,產(chǎn)生一筆日志,記錄下這個(gè)事件可能會(huì)讓這個(gè)方法更好些。

          6 異常的各個(gè)方面

            在Aspect Oriented Programming(AOP)的術(shù)語(yǔ)里,故障和應(yīng)變的處理是互相滲透的問(wèn)題。比如,要實(shí)施故障屏障的模式,所有參與的類(lèi)必須遵循通用規(guī)格:

          • 故障屏障方法必須存活在遍歷參與類(lèi)的方法調(diào)用圖的最前端
          • 參與類(lèi)必須使用未檢查的異常來(lái)表示故障情況
          • 參與類(lèi)必須使用故障屏障期望得到的有針對(duì)性的未檢查的異常類(lèi)型
          • 參與類(lèi)必須捕獲并從低端方法中把在執(zhí)行情境下注定的故障轉(zhuǎn)換成檢查的異常
          • 參與類(lèi)不能干擾故障異常被傳遞到故障屏障的過(guò)程

            這些問(wèn)題超越了那些本不相干的類(lèi)的邊界。結(jié)果就是少數(shù)零散的故障處理代碼,以及屏障類(lèi)和參與類(lèi)間暗含的耦合(這已經(jīng)比不使用模式進(jìn)步多了!)。AOP讓故障處理的問(wèn)題被封裝在通用的可以作用到參與類(lèi)的層面上。如AspectJ和Spring AOP這樣的Java AOP架構(gòu)認(rèn)為異常的處理是添加故障處理行為的切入點(diǎn)。這樣,把參與者綁定在故障屏障的模式可以放松些。故障的處理可以存活在一個(gè)獨(dú)立的,不相干的方面里,從而摒棄了屏障方法需要放在方法激活次序的最前頭的要求。

            如果在你的架構(gòu)里利用了AOP,故障和應(yīng)變的處理是理想的在應(yīng)用里用到的在方面上的候選。對(duì)故障和應(yīng)變的處理在AOP架構(gòu)下的使用做一個(gè)完整的勘探將是將來(lái)論文里一個(gè)很有意思的題目。

          7 結(jié)論

            雖然Java異常模型自它出現(xiàn)以來(lái)就激發(fā)了熱烈的討論,如果使用正確的話(huà),它的價(jià)值還是很大的。作為一個(gè)設(shè)計(jì)師,你的任務(wù)是建立好規(guī)格來(lái)最大限度地利用好這個(gè)模型。以故障和應(yīng)變的方式來(lái)考量異常可以幫助你做出正確的決定。合理使用好Java異常模型可以讓你的應(yīng)用簡(jiǎn)單,易維護(hù),和正確。AOP技術(shù)將故障和應(yīng)變定位為相互滲透的問(wèn)題,這個(gè)方法可能會(huì)對(duì)你的架構(gòu)提供一些幫助。

          8 引用

          • Sun's Exception Tutorial Java異常的基本知識(shí)
          • Does Java Need Checked Exception? Bruce Eckel對(duì)Java中檢查的異常的異議
          • Exceptional Java  關(guān)于異常的很好的討論,有架構(gòu)式的異常規(guī)則來(lái)模仿
          • The Exceptions Debate  來(lái)自于developerWorks的關(guān)于異常的來(lái)龍去脈
          • The Apache Struts Web Application Framework  Struts的信息源
          • The Spring Framework  Spring框架的信息源
          • Wikipedia: Aspect Oriented Programming  一個(gè)很好的對(duì)AOP概念的介紹
          • The AspectJ Project  AspectJ的信息源

          posted on 2009-02-12 22:29 suprasoft Inc,. 閱讀(318) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): J2SE
          ©2005-2008 Suprasoft Inc., All right reserved.
          主站蜘蛛池模板: 宁德市| 清远市| 都江堰市| 雷山县| 朝阳区| 阿拉善左旗| 邢台县| 巨鹿县| 湖州市| 罗定市| 福建省| 通辽市| 韩城市| 津南区| 木里| 仁寿县| 九江市| 湟源县| 淅川县| 信丰县| 庆安县| 城市| 施甸县| 牙克石市| 鱼台县| 兴义市| 汤阴县| 岗巴县| 临沭县| 邵阳县| 宣威市| 武功县| 新源县| 台中市| 周至县| 南溪县| 湖北省| 远安县| 明溪县| 内黄县| 饶河县|