程式設計原則

Zollty發表於2013-08-27

 

在寫方法(函式)的過程中,總結了如下原則:

 

【問題1】、有些方法,內容都差不多,只是引數的個數不一樣。此時,到底是寫多個方法呢,還是寫一個方法涵蓋所有功能(根據傳入的標識去執行不同的步驟)。

A、寫多個方法,每個方法引數不同

優點:每個方法執行一件事情,過程清晰明瞭,呼叫也簡單。

缺點:多個方法,有很多程式碼是冗餘的,而且要改起來很麻煩。

B、寫一個方法,涵蓋所有引數

優點:只需要編寫和維護一套程式碼。

缺點:呼叫時需要明確指定所有引數。需要做很多判斷,效能稍微低一點(可以忽略)。

》》》舉例:

改變字串編碼的方法:

按照A原則,可以寫兩個方法:

changeCode(String orgStr);

changeCode(String orgStr, String charSet);

但是按照B原則,則只需要寫一個方法:

changeCode(String orgStr, String charSet);

因為方法二的功能已經包括了方法一的功能。

 

如上所述,其實二者各有優劣,但是我更傾向於第二種方法。

因為:

對於呼叫者而言,只有一個方法供選擇,而且他必須顯式的指定第二個引數。對呼叫者而言,是透明的。

對於寫方法的人而言,注意到,他應該檢查引數的合法性,按照A原則,如果呼叫了第二個方法,但是第二個引數為空,那這個時候怎麼辦?

應該呼叫方法1呢,還是說直接報錯?那既然反正都要檢查這個charSet的引數,那何不制定一個規則:呼叫第2個方法,如果charSet為空,則執行第1個方法的流程。

 

總結:通常情況下,優先採用B方案,採用A方案的情景為

1)如果某些引數的方法使用非常頻繁,則可以單獨提出來,做成一個獨立的方法。

2)引數個數相同,但是引數型別不同(但是也可以用模糊型別的引數,在方法內再去判斷引數的型別)。

 

【問題2】、一個方法執行時走到了錯誤的流程,應該返回一個標識呢,還是throw一個錯誤?

 

A、返回一個標識,例如,執行失敗,則返回null、-1等。

優點:執行失敗時,返回null或者-1,呼叫者根據返回的標識即可判斷執行是否有錯。不需要try-catch。

缺點:通常,呼叫者不會去判斷返回的結果是什麼,它也不知道返回值代表的標識是什麼,所以當返回null、-1時,可能導致後期無法預知的錯誤。

B、throw一個錯誤,終止程式。

優點:無需定義錯誤標識,而且throw錯誤時,可以給出錯誤資訊。

缺點:呼叫者,需要用try-catch去捕獲錯誤,比較麻煩。

 

總結:對於那種公用的方法,還是採用方案B較好,可以約定,一律採用方案B

 

 

【問題3】、什麼樣的錯誤往外拋,什麼樣的錯誤內部處理?還有,錯誤日誌的記錄 應該在哪一層做處理(裡層還是外層)?

 

引入自定義 Runtime Exception: ZtyRuntimeException

 和自定義 Checked Exception: MedialException。

前者,自動中斷程式執行,預設情況下將堆疊資訊列印到控制檯,通過過載Thread.setDefaultUncaughtExceptionHandler方法,可以改變其策略,將執行時異常處理後再寫入日誌中。

    後者必須在外層程式中捕捉進行處理。最佳的流程是:如果不需要後續處理,則直接丟擲Runtime Exception,否則通過MedialException傳遞Exception到最外層,由最外層將錯誤程式碼和錯誤資訊展示或者傳給第三方系統。

     日誌的處理一定要定位準確,所以通常情況下,哪裡出錯,就在哪裡記錄日誌,不要在外層寫日誌,外層只負責處理錯誤後的程式邏輯,不負責寫日誌的工作。

一個例子:

D程式呼叫C程式,C程式呼叫B程式,B程式呼叫A程式,現在A程式出錯了。

情景一:

       D程式需要得到執行是否正確的回撥資訊,然後做相應處理。

那麼,錯誤處理應該為:A丟擲MedialException(errorCode, errorMsg),B、C和A一樣,D捕獲MedialException異常,將errorCode和errorMsg轉發到檢視層或者呼叫方。

情景二:

      D、C、B程式不需要回撥資訊,執行不成功只需要終止就行了。無論在哪裡終止都可以,框架會捕獲一個全域性異常資訊,然後顯示全域性錯誤頁面。

情景三:

      D、C、B、A流程環環相扣,聯絡緊密,但是都沒有需要顯示捕獲的異常,正常情況下,是不會出錯的。只有當出現未捕獲的RuntimeException時,才會導致程式異常終止,

這種終止是未曾預料到的,可能導致嚴重後果。

 

情景二分析:直接丟擲自定義的執行時異常ZtyRuntimeException,全域性捕獲到時,列印錯誤資訊就可以了。

情景三分析:首先我們應該分析,哪些步驟是關鍵性且不允許出現異常的(例如一個操作具有“要麼全部成功、要麼全部不成功”的特點時),需要儘量預料哪些地方容易出錯,進而捕獲錯誤。

其次,無論我們怎麼努力,都有一些無法預料的錯誤出現,那麼這個時候,我們只能考慮是否要將這些錯誤資訊記錄下來?如果要記錄下來,一種方式,是通過全域性的錯誤處理,另一種是針對性

的處理(例如:全域性處理是列印堆疊資訊到控制檯,但是如果某個功能出錯時你想要記錄日誌)。針對性處理,可以在入口處捕獲整個方法,然後做專門處理。

 

結合情景一、二、三,一個功能最齊全的處理流程是這樣的:

 

A丟擲MedialException錯誤資訊給B,同時A呼叫了兩個工具類,可能出現自定義的執行時異常ZtyRuntimeException和未捕獲的執行時異常RuntimeException(及其子類)

B丟擲MedialException錯誤資訊給C,

C丟擲MedialException錯誤資訊給D,D收到MedialException異常後,將errorCode和errorMsg傳送到頁面上。

 

由於D過程十分重要,不允許出現未知異常,但是未知異常是無法避免的,為了捕獲D在執行過程中出現未知異常,我們在D的整個方法上,加上了如下程式碼:

try{
   // ..... 整個D的方法
  }catch (TreRuntimeException e) {
    LOG.error( e.getMessage() );
  }catch (RuntimeException e) {
    LOG.error( e );
  }

這樣,即使D出現了未知錯誤,也可以記錄日誌,便於我們分析。其中對TreRuntimeException錯誤進行了區別對待,因為TreRuntimeException的錯誤資訊已經經過了加工,

它的errorMsg裡面自帶了一手堆疊資訊,故不需要像未知RuntimeException那樣記錄堆疊資訊了。

 

 

 

總結:對於那種公用的方法,還是採用方案B較好,可以約定,一律採用方案B

 

 

 

 

 

 

相關文章