持續重構,其樂無窮。
一:發現問題
先來說如何重構業務層的try{}catch{}finally{}程式碼塊,我看過很多程式碼,異常處理這一塊大致分為兩種情況,一種是每個方法都大量的充斥著try{}catch{}finally{ },這種方式的程式設計已經考慮到了異常處理,還有一種就是沒有try{ }catch{ }finally{ }的程式碼,因為根本就沒有考慮程式碼的異常處理。
每當我看到這樣的程式碼,我都很憂傷。從程式的健壯性來看第一種還是要比第二種情況好,至少在程式設計意識中,隨時想到了異常情況,有一種基本的程式設計思想。比如:一個業務單據的多表插入,關聯修改,虛擬刪除等都是一些基本的操作,但是又是比較容易引起錯誤的操作,在這些方法上都會加上try{ }catch{ }finally{}對程式碼進行有效的防錯處理。早期的程式碼是這樣的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public Boolean Save(AccountModel accountData) { Boolean result = false; try { //TODO ... result = true; } catch { } finally { } return result; } public Boolean Edit(AccountModel accountData) { Boolean result = false; try { //TODO ... result = true; } catch { } finally { } return result; } public Boolean VirDelete(AccountModel accountData) { Boolean result = false; try { //TODO ... result = true; } catch { } finally { } return result; } |
僅僅定義了新增,修改,刪除幾個空方法,就寫了三四十行程式碼,如果業務稍微複雜些,異常處理的程式碼很快就會突破百行大關。雖然複製,貼上try{}catch{}finally{}很好使,但是業務邏輯程式碼大量充斥著這樣的try{}catch{}finally{}程式碼,確實顯得做事不夠利落。
二:解決問題
那怎樣來解決這件棘手的事呢,首先定義一個公用的try{}catch{}finally{},如下如示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Process { public static bool Execute(Action action) { try { action.Invoke(); return true; } catch (Exception ex) { //1,異常隱藏 //2,異常替換 //3,異常封裝 //寫日誌 return false; } finally { } } } |
上邊的程式碼定義了公用的try{}catch{}finally{},最關鍵是怎麼運用起來,如下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
protected void Page_Load(object sender, EventArgs e) { AccountModel accountData = new AccountModel(); //準備傳入的引數 Boolean result = false; //接收返回的值 Process.Execute(() => result = Save(accountData)); //執行方法 } public Boolean Save(AccountModel accountData) { Boolean result = false; //TODO ... result = true; return result; } public Boolean Edit(AccountModel accountData) { Boolean result = false; //TODO ... result = true; return result; } public Boolean VirDelete(AccountModel accountData) { Boolean result = false; //TODO ... result = true; return result; } |
這樣的精簡過的程式碼,是不是感覺心情很舒暢。
三:提升與擴充套件
對於知足者常樂的人來說,到第二個步驟就可以洗洗睡了。但是對於精益求精的人來說,問題仍然沒有完。我們來說一個應用場景,在WCF中的應用,我們知道WCF服務端的異常,不經過的設定,服務端的異常是無法拋到客戶端的。但是在正式環境中,不可能對進行serviceDebug的配置。正確的處理是在服務端對異常進行隱藏,替換,或者封裝。
比如我們在服務端捕獲了一個已知異常,但是這個異常會暴露一些敏感的資訊,所以我們對異常進行替換,丟擲新的異常後,我們還要把這個異常怎樣傳輸給客戶端。首先們要明確WCF中的一些基本常識,就是WCF中的資料傳遞要遵循WCF的資料契約,服務端拋到客戶端的異常(異常其實也是資料),所以必須要給異常定義異常契約。
1 2 3 4 5 6 7 8 9 10 11 12 |
[DataContract(Name = "WCFException")] public class WCFException { [DataMember(Name = "Type")] public String Type { get; set; } [DataMember(Name = "StackTrace")] public String StackTrace { get; set; } [DataMember(Name = "Message")] public String Message { get; set; } } |
然後處理異常的公共方法改寫為:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public static bool Execute(Action action) { try { action.Invoke(); return true; } catch (Exception ex) { //1,異常隱藏 //2,異常替換 //3,異常封裝 //寫日誌 WCFException exception = new WCFException { Type = "Error" , StackTrace = ex.StackTrace , Message = ex.Message }; throw new FaultException(exception , new FaultReason("服務端異常:" + ex.Message) , new FaultCode(ex.TargetSite.Name)); } finally { } } |
這樣在服務端丟擲的異常,就能在客戶端捕捉到。現在是不是感覺自己又提升了一些,想成為程式設計高手是指日可待了。
四:舉一反三
異常的處理也不過如此,那是不是應該舉一反三,看看事務的處理應該怎麼辦?比如現在大量的訪求都用到了事務,如下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public Boolean Save(AccountModel accountData) { OracleConnection conn = new OracleConnection("連線字串"); IDbTransaction trans = conn.BeginTransaction(); Boolean result = false; try { //TODO ... trans.Commit(); result = true; } catch { trans.Rollback(); } finally { } return result; } |
特別是 trans.Commit(); trans.Rollback(); 這樣的程式碼出現在每個與事務相關的方法中, 讓我感覺到程式碼的臃腫,以及隱陷約約的失望。
經過我幾天的翻閱資料終於實現了事務的公用訪求提取。使用方法如下程式碼所示:
1 2 3 4 5 6 7 8 |
[TransactionAttribute] [ExceptionAttribute] public bool Save(DataContext dContext, Dictionarystring, string> dtoPara) { Boolean returnVal = true; //TODO ... return returnVal; } |
就是在一個方法上加[TransactionAttribute]就表示這個方法寫在了事務中,反之,不在事務中,加[ExceptionAttribute]就表示這個方法作了異常處理,反之,不作異常處理。通過反射或者AOP都能實現Attribute程式設計的效果。最後,還有什麼疑惑,可以在評論中給我留言。