Eclipse中自動重構實現探索 – JAVA編程語言程序開發技術文章

 本文用eclipse的自動重構功能對一個程序實例進行重構,目的是探索Eclipse自動重構可以在多大程度上輔助重構這個過程。程序實例使用《Refactoring:Improving the Design of Existing Code》一書中的例子。

   Eclipse的自動重構功能能夠很好地支持各種程序元素的重命名,並自動更新相關的引用。Eclipse能夠支持方法、字段在類之間移動,並自動更新引用。Eclipse較好地支持內聯字段、函數的更新替換。Eclipse較好地支持抽取方法、變量等程序元素。

   重構的過程是一個不斷嘗試和探索的過程。Eclipse的重構支持撤銷和重做,並且能夠預覽重構結果,這些是很實用的功能。

   Eclipse的重命名、抽取方法、移動、內聯功能、更改方法特征符等代碼結構級別的重構方法,是比較成熟同時也值得使用的功能。至於設計結構上的重構,eclipse還不能很好地支持。但是作者相信,自動重構的理念應該是”工具輔助下的重構工作”,人仍然承擔大部分重構工作。

   一、預備工作

   本文使用《Refactoring:Improving the Design of Existing Code》一書第一章的例子。重構前的代碼及每一步重構後的代碼見附件。讀者最好配合《Refactoring:Improving the Design of Existing Code》一書閱讀本文。

   Eclipse使用如下版本:


   同時安裝瞭中文語言包。

   二、重構第一步:分解並重組statement()

   目的:

   1、 把statement()函數中的swich語句提煉到獨立的函數amountFor()中。

   2、 修改amountFor()參數命名

   重構方法:

   Extract Method
   Rename Method

   方法:

   1、選中swich語句的代碼塊,在右鍵菜單中選擇”重構/抽取方法”,出現參數對話框。Eclipse自動分析代碼塊中的局部變量,找到瞭兩個局部變量:each和thisAmount。其中,each隻是在代碼塊中被讀取,但thisAmount會在代碼塊中被修改。按照重構Extract Method總結出來的規則,應該把each當作抽取函數的參數、thisAmount當作抽取函數的返回值。然而Eclipse並不做區分,直接把這兩個變量當作抽取新方法的參數,如圖。


   我們的目的是把在抽取函數中不會被修改的each作為參數;會被修改的thisAmount作為返回值。解決的辦法是,把 double thisAmount = 0; 這行代碼移到switch語句的上面,變成這樣:

double thisAmount = 0;
switch(each.getMovie().getPriceCode()){
  case Movie.REGULAR:
   thisAmount += 2;
   if(each.getDaysRented()>2)
   thisAmount += (each.getDaysRented()-2)*1.5;
   break;

  case Movie.NEW_RELEASE:
   thisAmount += each.getDaysRented()*3;
   break;

  case Movie.CHILDRENS:
   thisAmount += 1.5;
   if(each.getDaysRented()>3)
    thisAmount += (each.getDaysRented()-3)*1.5;
   break;
}

   選中這段代碼,在右鍵菜單中選擇”重構/抽取方法”,eclipse這次變得聰明點瞭,如圖。


   選擇”預覽”按鈕預先查看重構後的結果,符合我們最初的目的。


   選擇”確定”按鈕,重構後的代碼片斷如下:

public String statement() {
  double totalAmount = 0;
  int frequentRenterPoints = 0;
  Enumeration rentals = _rentals.elements();
  String result = “Rental Record for ” + getName() + ”
“;

  while(rentals.hasMoreElements()){
   Rental each = (Rental)rentals.nextElement();

  double thisAmount = amountFor(each);

   frequentRenterPoints ++;
   if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE &&each.getDaysRented()>1)
    frequentRenterPoints ++;

    result += ” ” + each.getMovie().getTitle() + ” ” +String.valueOf(thisAmount) + ”
“;
    totalAmount += thisAmount;
  }

  result += “Amount owed is ” + String.valueOf(totalAmount) + ”
“;
  result += “You earned ” + String.valueOf(frequentRenterPoints) + ” frequent renter points”;

  return result;
}

/**
* @param each
* @return
*/
private double amountFor(Rental each) {
  double thisAmount = 0;
  switch(each.getMovie().getPriceCode()){
   case Movie.REGULAR:
    thisAmount += 2;
    if(each.getDaysRented()>2)
     thisAmount += (each.getDaysRented()-2)*1.5;
    break;

   case Movie.NEW_RELEASE:
    thisAmount += each.getDaysRented()*3;
    break;

   case Movie.CHILDRENS:
    thisAmount += 1.5;
    if(each.getDaysRented()>3)
     thisAmount += (each.getDaysRented()-3)*1.5;
    break;
  }
  return thisAmount;
}

   2、選中amountFor()的參數each,在右鍵菜單中選擇”重構/重命名”,在對話框中輸入新的名稱:aRental,選擇確定,amountFor()中所有each的引用全部被替換成新的名稱。用同樣的辦法修改amountFor()中的局部變量thisAmount為result。重構後的amountFor()代碼如下:

/**
* @param aRental
* @return
*/
private double amountFor(Rental aRental) {
  double result = 0;
  switch(aRental.getMovie().getPriceCode()){
   case Movie.REGULAR:
   result += 2;
    if(aRental.getDaysRented()>2)
    result += (aRental.getDaysRented()-2)*1.5;
    break;

   case Movie.NEW_RELEASE:
   result += aRental.getDaysRented()*3;
    break;

   case Movie.CHILDRENS:
   result += 1.5;
    if(aRental.getDaysRented()>3)
     result += (aRental.getDaysRented()-3)*1.5;
    break;
  }
  return result;
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *