久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

JAVA異常設(shè)計原則 - Java綜合 - Java - JavaEye論壇

 techres 2011-03-15
異常是面向?qū)ο笳Z言非常重要的一個特性,,良好的異常設(shè)計對程序的可擴展性、可維護(hù)性、健壯性都起到至關(guān)重要,。
JAVA根據(jù)用處的不同,定義了兩類異常
    * Checked Exception: Exception的子類,方法簽名上需要顯示的聲明throws,,編譯器迫使調(diào)用者處理這類異?;蛘呗暶鱰hrows繼續(xù)往上拋。
    * Unchecked Exception: RuntimeException的子類,,方法簽名不需要聲明throws,,編譯器也不會強制調(diào)用者處理該類異常。

異常的作用和好處:
1. 分離錯誤代碼和正常代碼,,代碼更簡潔,。
2. 保護(hù)數(shù)據(jù)的正確性和完整性,程序更嚴(yán)謹(jǐn)。
3. 便于調(diào)試和排錯,,軟件更好維護(hù),。
……

相信很多JAVA開發(fā)人員都看到或聽到過“不要使用異常來控制流程”,雖然這句話非常易于記憶,,但是它并未給出“流程”的定義,,所以很難理解作者的本意,讓人迷惑不解,。

如果“流程”是包括程序的每一步執(zhí)行,,我認(rèn)為異常就是用來控制流程的,它就是用來區(qū)分程序的正常流程和錯誤流程,,為了更能明確的表達(dá)意思,,上面這 句話應(yīng)改成“不要用異常來控制程序的正常流程”。現(xiàn)在帶來一個新的問題就是如何區(qū)分程序正常流程和異常流程,?我實在想不出一個評判標(biāo)準(zhǔn),,就舉例來說明,大 家思維擴散下,。

為了后面更方便的表達(dá),,我把異常分成兩類,不妥之處請諒解

    * 系統(tǒng)異常:軟件的缺陷,,客戶端對此類異常是無能為力的,,通常都是Unchecked Exception。
    *業(yè)務(wù)異常:用戶未按正常流程操作導(dǎo)致的異常,,都是Checked Exception

金幣轉(zhuǎn)帳例子
1. 需求規(guī)定金幣一次的轉(zhuǎn)賬范圍是1~500,,如果超過這個額度,就要提示用戶金額超出單筆轉(zhuǎn)賬的限制,,轉(zhuǎn)賬的金額是由用戶在頁面輸入的:
因為值是用戶輸入的,,所以給的值超出限定的范圍肯定是司空見慣。我們當(dāng)然不能把它(輸入的值超出限定的范圍)歸結(jié)于異常流程,,它應(yīng)該屬于正常流程,,應(yīng)該提供驗證數(shù)據(jù)的完整功能。
正確的實現(xiàn)如下:
提供一個判斷轉(zhuǎn)賬金幣數(shù)量是否超出限定范圍的方法

Java代碼  收藏代碼
  1. private static final int MAX_PRE_TRANSFER_COIN = 500;  
  2.   
  3. public boolean isCoinExceedTransferLimits(int coin) {  
  4. return coin > MAX_PRE_TRANSFER_COIN;  
  5. }  


Action里先對值進(jìn)行校驗,,若不合法,,直接返回并提示用戶

2. 在轉(zhuǎn)賬的過程里,發(fā)些金幣數(shù)量不夠:
我們的程序都是運行在并發(fā)環(huán)境中,,Action無法完全做到判斷金幣是否足夠,。因為在判斷之后和事務(wù)之前的剎那間,有可能產(chǎn)生其他扣費操作導(dǎo)致金幣不夠,。這時我們就需要用業(yè)務(wù)異常(Checked Exception)來控制

正確的實現(xiàn)如下
CoinNotEnoughExcetion .java
Java代碼  收藏代碼
  1. //金幣不夠的異常類  
  2. public class CoinNotEnoughExcetion extends Exception {  
  3. private static final long serialVersionUID = -7867713004171563795L;  
  4. private int coin;  
  5. public CoinNotEnoughExcetion() {  
  6. }  
  7.   
  8. public CoinNotEnoughExcetion(int coin) {  
  9. this.coin = coin;  
  10. }  
  11.   
  12. public int getCoin() {  
  13. return coin;  
  14. }  
  15.   
  16. @Override  
  17. public String getMessage() {  
  18. return coin + " is exceed transfer limit:500";  
  19. }  
  20. }  

//轉(zhuǎn)賬方法
Java代碼  收藏代碼
  1. private static final int MAX_PRE_TRANSFER_COIN = 500;  
  2.   
  3. public void transferCoin(int coin) throws CoinNotEnoughExcetion{  
  4. if (!hasEnoughCoin())  
  5. throw new CoinNotEnoughExcetion(coin);  
  6. // do transfering coin  
  7. }  



3. 接口transferCoin(int coin)的規(guī)范里已經(jīng)定了契約,,調(diào)用transferCoin之前必須要先調(diào)用isCoinExceedTransferLimits判斷值是否合法:
雖然規(guī)范人人都要遵循,,但畢竟只是規(guī)范,編譯器無法強制約束,。此時就需要用系統(tǒng)異常(Unchecked Exception,,用JDK的標(biāo)準(zhǔn)異常)來保證程序的正確性,沒遵守規(guī)范的都當(dāng)做軟件bug處理,。
正確的實現(xiàn)如下:

//轉(zhuǎn)賬方法
Java代碼  收藏代碼
  1. public void transferCoin(int coin){  
  2. if (coin > MAX_PRE_TRANSFER_COIN)  
  3. throw new IllegalArgumentException(coin+" is exceed tranfer limits:500");  
  4. // do transfering coin  
  5. }  


至此,,舉例已經(jīng)結(jié)束了,在這里再延伸下—業(yè)務(wù)異常和系統(tǒng)異常類在實現(xiàn)上的區(qū)別,,該區(qū)別的根源來自于調(diào)用者捕獲到異常后是如何處理的

Action對業(yè)務(wù)異常的處理:操作具體的異常類

Java代碼  收藏代碼
  1. public String execute() {  
  2. try {  
  3. userService.transferCoin(coin);  
  4. catch (CoinExceedTransferLimitExcetion e) {  
  5. e.getCoin();  
  6. }  
  7. return SUCCESS;  
  8. }  


Action對系統(tǒng)異常的處理:無法知道具體的異常類
Java代碼  收藏代碼
  1. public String execute() {  
  2. try {  
  3. userService.transferCoin(coin);  
  4. catch (RuntimeException e) {  
  5. LOG.error(e.getMessage());  
  6. }  
  7. return SUCCESS;  
  8. }  


調(diào)用者捕獲業(yè)務(wù)異常(Checked Excetion)之后,,通常不會去調(diào)用getMessage()方法的,而是調(diào)用異常類里特有的方法,,所以業(yè)務(wù)異常類的實現(xiàn)要注重特有的,,跟業(yè)務(wù)相關(guān)的方法,而不是getMessage()方法,。
系統(tǒng)異常類恰恰相反,,捕獲者只會調(diào)用getMessage()獲取異常信息,然后記錄錯誤日志,,所以系統(tǒng)異常類(Uncheck Exception)的實現(xiàn)類對getMessage()方法返回內(nèi)容比較講究,。

不管是業(yè)務(wù)異常還是系統(tǒng)異常,都需要提供豐富的信息,。如:數(shù)據(jù)庫訪問異常,,需要提供查詢sql語句等;HTTP接口調(diào)用異常,,需要給出訪問的URL和參數(shù)列表(如果是post請求,,參數(shù)列表不提供也可以,取決于維護(hù)人員或者開發(fā)人員拿到參數(shù)列表會如何去使用),。
1. Sql語法錯誤異常類(取自spring框架的異常類,,Spring的異常體系很強大,值得一看):

Java代碼  收藏代碼
  1. public class BadSqlGrammarException extends InvalidDataAccessResourceUsageException {  
  2. private String sql;  
  3.   
  4. public BadSqlGrammarException(String task, String sql, SQLException ex) {  
  5. super(task + "; bad SQL grammar [" + sql + "]", ex);  
  6. this.sql = sql;  
  7. }  
  8.   
  9. public SQLException getSQLException() {  
  10. return (SQLException) getCause();  
  11. }  
  12.   
  13. public String getSql() {  
  14. return this.sql;  
  15. }  
  16. }  


2. HTTP接口調(diào)用異常類

Java代碼  收藏代碼
  1. public class HttpInvokeException extends RuntimeException {  
  2. private static final long serialVersionUID = -6477873547070785173L;  
  3.   
  4. public HttpInvokeException(String url, String message) {  
  5. super("http interface unavailable [" + url + "];" + message);  
  6. }  
  7. }  


如何選擇用Unchecked Exception和Checked Exception
1.是軟件bug還是業(yè)務(wù)異常,,軟件bug是Unchecked Exception,否則是Checked Exception
2.如果把該異常拋給用戶,,用戶能否做出補救。如果客戶端無能為力,,則用Unchecked Exception,,否則拋Checked Exception
結(jié)合這兩點,兩類異常就不會混淆使用了,。


異常設(shè)計的幾個原則:
1.如果方法遭遇了一個無法處理的意外情況,那么拋出一個異常,。
2.避免使用異常來指出可以視為方法的常用功能的情況,。
3.如果發(fā)現(xiàn)客戶違反了契約(例如,,傳入非法輸入?yún)?shù)),那么拋出非檢查型異常,。
4.如果方法無法履型契約,,那么拋出檢查型異常,也可以拋出非檢查型異常,。
5.如果你認(rèn)為客戶程序員需要有意識地采取措施,,那么拋出檢查型異常。
6.異常類應(yīng)該給客戶提供豐富的信息,,異常類跟其它類一樣,,允許定義自己的屬性和方法。
7.異常類名和方法遵循JAVA類名規(guī)范和方法名規(guī)范
8.跟JAVA其它類一樣,,不要定義多余的方法和變量,。(不會使用的變量,就不要定義,spring的BadSqlGrammarException.getSql() 就是多余的)


以下是我工作當(dāng)中碰到的一些我認(rèn)為不是很好的寫法,,我之前也犯過此類的錯誤
A. 整個業(yè)務(wù)層只定義了一個異常類
Java代碼  收藏代碼
  1. public class ServiceException extends RuntimeException {  
  2. private static final long serialVersionUID = 8670135969660230761L;  
  3.   
  4. public ServiceException(Exception e) {  
  5. super(e);  
  6. }  
  7.   
  8. public ServiceException(String message) {  
  9. super(message);  
  10. }  
  11. }  


理由:
1.業(yè)務(wù)異常不應(yīng)該是Unchecked Exception,。
2.不存在一個具體的異常類名稱是“ServiceException”。

解決方法:定義一個抽象的業(yè)務(wù)異常“ServiceException”
Java代碼  收藏代碼
  1. public abstract class ServiceException extends Exception {  
  2. private static final long serialVersionUID = -8411541817140551506L;  
  3. }  


B. 忽略異常
Java代碼  收藏代碼
  1. try {  
  2. new String(source.getBytes("UTF-8"), "GBK");  
  3. catch (UnsupportedEncodingException e) {  
  4. e.printStackTrace();  
  5. }  


理由:
1.環(huán)境不支持UTF-8或者GBK很顯然是一個非常嚴(yán)重的bug,,不能置之不理
2.堆棧的方式記錄錯誤信息不合理,,若產(chǎn)品環(huán)境是不記錄標(biāo)準(zhǔn)輸出,這個錯誤信息就會丟失掉,。若產(chǎn)品環(huán)境是記錄標(biāo)準(zhǔn)輸出,,萬一這段程序被while循環(huán)的線程調(diào)用,有可能引起硬盤容量溢出,,最終導(dǎo)致程序的運行不正常,,甚至數(shù)據(jù)的丟失。

解決方法:捕獲UnsupportedEncodingException,,封裝成Unchecked Exception,,往上拋,中斷程序的執(zhí)行,。
Java代碼  收藏代碼
  1. try {  
  2. new String(source.getBytes("UTF-8"), "GBK");  
  3. catch (UnsupportedEncodingException e) {  
  4. throw new IllegalStateException("the base runtime environment does not support 'UTF-8' or 'GBK'");  
  5. }  


C. 捕獲頂層的異?!狤xception
Java代碼  收藏代碼
  1. public void transferCoin(int outUid, int inUserUid, int coin) throws CoinNotEnoughException {  
  2. final User outUser = userDao.getUser(outUid);  
  3. final User inUser = userDao.getUser(inUserUid);  
  4. outUser.decreaseCoin(coin);  
  5. inUser.increaseCoin(coin);  
  6. try {  
  7. // BEGIN TRANSACTION  
  8. userDao.save(outUser);  
  9. userDao.save(inUser);  
  10. // END TRANSACTION  
  11. // log transfer operate  
  12. catch (Exception e) {  
  13. throw new ServiceException(e);  
  14. }  
  15. }  

理由:
1. Service并不是只能拋出業(yè)務(wù)異常,Service也可以拋出其他異常
如IllegalArgumentException,、ArrayIndexOutOfBoundsException或者spring框架的DataAccessException
2. 多數(shù)情況下,,Dao不會拋Checked Exception給Service,假如所有代碼都非常規(guī)范,,Service類里不應(yīng)該出現(xiàn)try{}catch代碼,。

解決方法:刪除try{}catch代碼
Java代碼  收藏代碼
  1. public void transferCoin(int outUid, int inUserUid, int coin) throws CoinNotEnoughException {  
  2. final User outUser = userDao.getUser(outUid);  
  3. final User inUser = userDao.getUser(inUserUid);  
  4. outUser.decreaseCoin(coin);  
  5. inUser.increaseCoin(coin);  
  6. // BEGIN TRANSACTION  
  7. userDao.save(outUser);  
  8. userDao.save(inUser);  
  9. // END TRANSACTION  
  10. // log transfer operate  
  11. }  


D. 創(chuàng)建沒有意義的異常
Java代碼  收藏代碼
  1. public class DuplicateUsernameException extends Exception {  
  2. }  

理由
1. 它除了有一個"意義明確"的名字以外沒有任何有用的信息了。不要忘記Exception跟其他的Java類一樣,,客戶端可以調(diào)用其中的方法來得到更多的信息,。

解決方案:定義上捕獲者需要用到的信息
Java代碼  收藏代碼
  1. public class DuplicateUsernameException extends Exception {  
  2. private static final long serialVersionUID = -6113064394525919823L;  
  3. private String username = null;  
  4. private String[] availableNames = new String[0];  
  5.   
  6. public DuplicateUsernameException(String username) {  
  7. this.username = username;  
  8. }  
  9.   
  10. public DuplicateUsernameException(String username, String[] availableNames) {  
  11. this(username);  
  12. this.availableNames = availableNames;  
  13. }  
  14.   
  15. public String requestedUsername() {  
  16. return this.username;  
  17. }  
  18.   
  19. public String[] availableNames() {  
  20. return this.availableNames;  
  21. }  
  22. }  

E. 把展示給用戶的信息直接放在異常信息里,。
Java代碼  收藏代碼
  1. public class CoinNotEnoughException2 extends Exception {  
  2. private static final long serialVersionUID = 4724424650547006411L;  
  3.   
  4. public CoinNotEnoughException2(String message) {  
  5. super(message);  
  6. }  
  7. }  
  8.   
  9. public void decreaseCoin(int forTransferCoin) throws CoinNotEnoughException2 {  
  10. if (this.coin < forTransferCoin)  
  11. throw new CoinNotEnoughException2("金幣數(shù)量不夠");  
  12. this.coin -= forTransferCoin;  
  13. }  


理由:展示給用戶錯誤提示信息屬于文案范疇,文案易變動,,最好不要跟程序混淆一起,。
解決方法:
錯誤提示的文案統(tǒng)一放在一個配置文件里,根據(jù)異常類型獲取對應(yīng)的錯誤提示信息,,若需要支持國際化還可以提供多個語言的版本,。

F. 方法簽名聲明了多余的throws
理由:代碼不夠精簡,調(diào)用者不得不加上try{}catch代碼
解決方案:若方法不可能會拋出該異常,,那就刪除多余的throws

G. 給每一個異常類都定義一個不會用到ErrorCode
理由:多一個功能就多一個維護(hù)成本
解決方法:不要無謂的定義ErrorCode,,除非真的需要(如給別人提供接口調(diào)用的,這時,,最好對異常進(jìn)行規(guī)劃和分類,。如1xx代表金幣相關(guān)的異常,2xx代表用戶相關(guān)的異常…

最后推薦幾篇關(guān)于異常設(shè)計原則
1.異常設(shè)計
http://www.cnblogs.com/JavaVillage/articles/384483.html(翻譯)
http://www./jw-07-1998/jw-07-techniques.html(原文)

2. 異常處理最佳實踐
http://tech./articles/2009/79/1247105040929_1.html(翻譯)
http:///pub/a/onjava/2003/11/19/exceptions.html(原文)

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多