設計迷蹤:給JAVA設計開發新手的一些建議和意見(二)

飛雲小俠 2005-7-23 http://www.jscud.com 轉載請註明作者


【處理好你的異常】
-----------------

 異常處理是Java編程中非常重要的一個部分.建議在使用異常之前閱讀<Effective Java Programming Language Guide>或者<Practical Java>.
 
 下面從書中摘出幾條建議:
  *絕對不要忽略異常
  *千萬不要隱藏異常***
  *僅在不正常的情況下使用異常
  *對可恢復的情況使用可檢查異常,對程序錯誤使用運行時異常(RunTimeException)
  *給方法引發的異常做文檔
  *在詳細信息裏面包括失敗捕獲信息
  *使用finally避免資源泄漏
  *....
  
 在這裏特別提出的是,在開發中要特別處理NULL的情況,否則經常引發NullPointException異常,在Java裏這是一個最令人頭疼的異常了.
 如果你的程序因爲一個NULL值,而報了幾十個NullPointException的話,不但得讓人煩死,而且還非常難以找到錯誤所在.所以在Java中一定要注意這個問題.
 如果你的函數不允許Null值,那麼可以截獲它,拋出一個異常,或者給客戶更友好的提示,難道不好嗎?
 
 讓我們來看一個例子:

  public String getName(User aUser)
 {
  //如果aUser爲Null,會發生什麼情況
  return aUser.getName();
 }

  
 很明顯,如果參數爲Null,就會拋出異常.應該改爲:

 public String getName(User aUser)
 {
  if(null=aUser)
  {
   return "";
  }  
  else
  {
   return aUser.getName();
  }
 }
 


  
 或者你要求參數不能爲空,還可以拋出一個異常,強制使用者不能傳入空值.
 
 
 
 
 還有經常被忽略的是RunTimeException和普通異常的區別,在Java中,這是一個特殊的異常類,程序中如果遇到這個異常,用戶可以不截獲它,而如果是其他的普通異常,就不許要截獲它.我們的代碼經常這麼寫:
 

 try
 {
  //your code here
 }
 catch(Exception e)
 {
  //do warn
 }

 
 
 這樣寫的話,就截獲了所有異常,當然也包括了RunTimeException. 在很多情況下,這是不合適的處理方式,我們只應截獲必要的異常,而應該忽略RuntimeException.
 
 關於RunTimeException,在Spring中還有更好的利用方式,建議閱讀Spring框架中在事務中對異常的處理代碼,例如對Jdbc拋出的SqlException的轉換.
 
 關於異常處理,我提出幾點建議:
  *捕獲異常而且再次拋出時要包含原來的異常信息
  *不要忘了RunTimeException,除非必要,否則不要用catch(Exception e)的方式捕獲所有異常.
  *不要用異常做流程控制,異常的性能代價比較高昂.(對此,可能有人不同意.此處不詳細討論)
  *不要把異常處理都拋給別人,本函數有能力處理的就不要拋出.
 
 在此建議讀者詳細閱讀<Effective Java Programming Language Guide>或者<Practical Java>.

 

【過度依賴】

 在定位錯誤的時候,經常遇到瀏覽了七 八個文件還是沒有找到什麼地方執行了真正需要的函數,這個時候就非常鬱悶.A調用了B,B調用了C,C調用了D......讓人找不到北
 
 面對這樣的程序,存在的問題不僅僅是定位錯誤麻煩,而且如果需要維護這樣的函數庫/框架,恐怕你的有非常高的統御能力才行,否則打死我也不去維護.
 
 那麼我們自己最好不要寫這樣的程序出來給人用.


【濫用接口】

 現在流行"面對接口編程",這本身本來是不錯,但是濫用接口的現象卻經常發生.
 "面向接口",於是所有的類都有一個對應的接口,接口的函數聲明和類一模一樣,而且一個接口只有一個類來實現它.這樣的面向接口有什麼意義哪? (爲了用Spring的事務的情況除外)
 
 根據"迪比特法則(Law of Demter)",一個對象應當對其他對象有儘可能少的瞭解.一個接口內應該只定義對方所需要的方法,而不要把一些沒用的方法聲明放在接口裏面.
 
 例如如下一個類:
 

  public class MyCounter
  {
   private int n1;
   private int n2;
   public MyCounter(int n1,int n2)
   {
    this.n1=n1;
    this.n2=n2;    
   }
   
   public void setN1(int n1)
   {
    return this.n1 = n1;
   }
   public void setN2(int n2)
   {
    return this.n2 = n2;
   }
   public int getN1()
   {
    return n1;
   }
   public int getN2()
   {
    return n2;
   }
   
   public int getResult()
   {
    return n1 + n2;
   }   
  }
  
  
 我們可以看到,這個類的主要目的是得到計算結果,所以正確的接口應該類似:
 
  
  public interface Counter
  {
   int getResult();
  } 
  
 
 但是很多情況下,經常是這樣的接口:
 
 
  public interface Counter
  {
   int getResult();
   int getN1();
   int getN2();
   void setN1(int n1);
   void setN2(int n2);
  } 
  
  
 我們想一想,這樣做有2個後果:
  1.除了getResult之外,其他的函數我們根本用不到,所以是多餘的.
  2.如果我們要自己實現一個Counter,如果接口中僅僅定義了getResult,我們僅僅需要實現它就可以了.我們自己的類可能是多個數運算,有乘除加減等等各種運算,參數也有可能是一些數組.但是如果按照第二種方法聲明接口的話,我們就必須實現後面的四個方法,如果這樣的話,實現這樣東西不僅沒用,而且浪費時間.我們恐怕要大聲罵娘了吧.
 
 
 所以,接口有好的作用,但是不要濫用.
 ■ 如果你的接口永遠只有一個類實現,那麼可能就沒有必要用接口.
 ■ 你的接口只需要聲明別人用到的函數即可.
  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章