熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

診斷和糾正 Java 程序中反復出現的錯誤類型

2013-11-15 11:50:55  來源: JSP教程 

  歡迎光臨診斷 Java 代碼一個隔周更新的新專欄著重討論和您日常編程工作有關的 Java 解決方案本文為第一篇介紹了錯誤模式的概念一個非常有用的概念它將提高您檢測和修正代碼中錯誤的能力您會了解到一種最普遍的錯誤模式這將為您開始識別和避免更高級的錯誤模式奠定基礎
  
  
  
  錯誤模式和它們為什麼有用
  
  
  
  正如好的編程技能涉及很多設計模式(您可以在不同的程序上下文中組合和應用這些模式)的知識一樣好的調試技能也涉及對錯誤模式的一定了解錯誤模式就是已發出的錯誤和程序中潛在的錯誤之間的重復出現的相互關系這種概念對編程來說並不新鮮醫生們在診斷疾病時依靠相似類型的相互關系他們在實習期間通過和資格較老的醫生共同工作來學習這些他們的教育就是集中在做這種診斷上的相反我們軟件工程師的教育是集中在過程設計和算法分析上的這些技能固然重要但是人們對調試過程的教育卻很少關注相反我們得自己去拾起這種技能隨著極端編程的出現和它對單元測試的注重這種做法已經開始改變了但是頻繁的單元測試只是解決了問題的一部分一旦發現錯誤就必須診斷和糾正它們幸運的是很多錯誤都遵循我們可以識別的幾種錯誤模式的其中一種一旦您可以識別出這些錯誤模式您就可以診斷出錯誤的原因並且更快地糾正它了
  
  
  
  錯誤模式與反模式有關反模式是一次又一次被證明是失敗的公共軟件設計的模式雖然反模式是設計模式錯誤模式卻是與編程錯誤相關的錯誤的程序行為的模式這與設計根本沒有關系而是與編程和調試過程有關
  
  
  
  通過示例學習
  
  為了說明錯誤模式後面的思想讓我們來考慮一種基本錯誤模式編程新手(經常還有更高級的程序員)常常會遇到這種錯誤模式在後面的文章中我們會談到更高級的錯誤模式對每一種模式我會討論將有助於把該模式的錯誤的發生控制到最少的編程原則(並非暗示所有的錯誤都是不遵循編程原則的結果不管我們遵循多少原則我們都會犯錯誤)
  
  
  
  為了分類起見我會使用下面的形式(從醫學上借用一些術語)來概括錯誤模式描述
  
  
  
  模式名稱
  
  症狀
  
  起因
  
  治療方法和預防措施
  
  Rogue Tile 模式
  
  也許它是編程新手中最普遍的錯誤模式起因是復制和粘貼一段代碼到程序的其它部分有時復制的一小部分因為功能上需求的略微不同而作了改動不可避免地錯誤在一個副本中被修正了而在另一個副本中沒有被修正這樣在錯誤症狀復發時就會讓您很頭疼盡管大多數程序員很快就熟悉了這種錯誤模式但他們中很少人采取適當的措施來將這種錯誤的出現控制到最少您很容易就會偷懶不去思考而簡單地復制您認為已經可以運行的代碼但是工作效率由於修正代碼而喪失這是因為不加選擇的復制—粘貼操作很快降低了復制代碼帶來的任何工作效率
  
  
  
  我稱此為 Rogue Tile 模式是因為一段代碼的各個副本可以被看成是分布在程序中的tile由於不同副本中的代碼出現了差異副本就變成了rogue tile
  
  
  
  症狀
  
  這種錯誤的模式的最普遍症狀是在您認為已經修正了問題以後程序還繼續表現出錯誤的行為
  
  
  
  起因
  
  為了理解這種情況發生的原因我們來看看下面的二元樹類層次結構
  
  
  
  
  
  public abstract class Tree {
  
  
  
  }
  
  
  
  public class Leaf extends Tree {
  
  
  
  public Object value;
  
  
  
  }
  
  
  
  public class Branch extends Tree {
  
  
  
  public Object value;
  
  public Tree left;
  
  public Tree right;
  
  
  
  }
  
  
  
  
  
  
  
  
  
  對於這些類要注意的第一件事就是兩種具體類都包含 Object 類型的 value 字段如果您決定稍後讓樹包含比如說Interger您也許會忘記更新其中的一個字段聲明如果程序的其它部分需要這些字段是 Interger 的話程序就很可能不會編譯您或許記得您改變了其中一個類的 value 字段的類型卻忽略了一個事實就是您沒有在其它類中作相應的改變
  
  
  
  一些預防措施
  
  當然這個示例所示的錯誤是編程新手可以很快學會通過分解出公共代碼來避免的在本例中字段聲明應該移到 Tree 類中它的兩個子類就會繼承這個字段而且對字段聲明的任何改變都只需要在一個地方出現
  
  
  
  繼續看這個示例我們可能還會編寫在一個 Tree 中相加和相乘所有節點的方法為了簡單起見我將以遞歸的方式來編寫這些方法
  
  
  
  
  
  // in class Tree:
  
  
  
  public abstract int add();
  
  public abstract int multiply();
  
  
  
  // in class Branch:
  
  
  
  public int add() {
  
  return thisvalueintValue() + leftadd() + rightadd();
  
  }
  
  
  
  public int multiply() {
  
  return thisvalueintValue() * leftmultiply() + rightmultiply();
  
  }
  
  
  
  // in class Leaf:
  
  
  
  public int add() { return thisvalueintValue(); }
  
  public int multiply() { return thisvalueintValue(); }
  
  
  
  
  
  
  
  
  
  請注意我在 multiply 方法中為 Branch 類引入的錯誤我沒有用第三項去乘而是加了它錯誤發生了因為我通過復制 add 方法中的代碼並作輕微(但不完全)的改動創建了 multiply 方法這種錯誤非常隱蔽因為調用 multiply 方法永遠不會發出錯誤信號事實上在很多情況下它會返回一個看上去完全合理的結果
  
  
  
  就象以前一樣我們可以通過分解出公共代碼來將這種錯誤控制到最少在這種情況下我們可以編寫一個單獨的方法它在 Tree 上累計一個運算符(作為一個參數傳送)我們可以使用一種被稱為公共模式的設計模式(不是錯誤模式!)在對象中封裝這個運算符
  
  
  
  
  
  public abstract class Operator {
  
  public abstract int apply(int l int r);
  
  }
  
  
  
  public class Adder extends Operator {
  
  public int apply(int l int r) {
  
  return l + r;
  
  }
  
  }
  
  
  
  public class Multiplier extends Operator {
  
  public int apply(int l int r) {
  
  return l * r;
  
  }
  
  }
  
  
  
  
  
  
  
  
  
  然後我們就可以如下面的代碼所示在我們的 Tree 類層次結構中改變這個方法
  
  
  
  
  
  // in class Tree:
  
  
  
  public abstract int accumulate(Operator o);
  
  
  
  public int add() {
  
  return thisaccumulate(new Adder());
  
  }
  
  
  
  public int multiply() {
  
  return thisaccumulate(new Multiplier());
  
  }
  
  
  
  // in class Leaf:
  
  
  
  public int accumulate(Operator o) {
  
  return valueintValue();
  
  }
  
  
  
  
  
  in class Branch:
  
  
  
  public int accumulate(Operator o) {
  
  return oapply(thisvalueintValue()
  
  oapply(leftaccumulate(o)
  
  rightaccumulate(o)));
  
  }
  
  
  
  
  
  
  
  
  
  通過分解出公共代碼我們消除了在 add 和 multiply 方法正文中出現復制—粘貼錯誤的可能性另外請注意我們不再需要為 Tree 的每一個子類編寫單獨的 add 和 multiply 方法了
  
  
  
  分解出公共代碼是一個很好的習慣但它並不適用於所有的情況比如說Java 類型系統的簡單性經常迫使我們在精確類型檢驗和保持對程序的每個不同的功能性元素的單點控制(請參閱參考資料閱讀我寫的關於 NextGen 的文章)之間作出選擇正因為這個Rogue Tile 模式是所有開發人員必須一直努力以控制到最少的一種錯誤類型
  

From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19742.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.