[軟體工程] 作業三_軟體程式重構(Refactoring)與先測試(Test-first develompent)開發流程

Q1:請列舉出至少5項Refactoring的方式。-- From 中正大學 熊博安教授  實驗室

A1:

重構的主要目的是使程式碼變簡單,質量更高、更有效率,同時保持架構清楚且有邏輯,令人一目了然。以下是程式碼常見的問題與重構的應用。


A. 保持物件完整

問題: 從某個物件中取值,再把它們變成其他函數的輸入值。
int low = daysTempRange.getLow();
int high = daysTempRange.getHigh();
boolean withinPlan = plan.withinRange(low, high);

解決: 改成傳遞整個物件,可以讓程式碼更精簡,而不是定義多行的程式碼
boolean withinPlan = plan.withinRange(daysTempRange);



B. 以物件取代陣列

問題: 有一個陣列裡面的元素代表不同的東西。例如:第0是Liverpool、第1是15
String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";

解決: 以物件代替陣列。對於陣列中的每一個元素以一個欄位表示。例如:setName為Liverpool、setWins為15
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");



C. 提煉函數

問題: 一段程式碼中,有一些函數可以整合在一起。
void printOwing() {
  printBanner();

  //print details
  System.out.println("name: " + name);
  System.out.println("amount: " + getOutstanding());
}

解決: 移動這段程式碼到一個新的函數中,呼叫函數來替代原有的程式碼
void printOwing() {
  printBanner();
  printDetails(getOutstanding());
}
void printDetails(double outstanding) {
  System.out.println("name: " + name);
  System.out.println("amount: " + outstanding);
}



D. 分解條件表示式

問題: 條件式很複雜,無法讓人一眼看出其中的條件判斷。
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
  charge = quantity * winterRate + winterServiceCharge;
}else {
  charge = quantity * summerRate;
}

解決: 將條件拆解成幾個函數,可以清楚了解其中的邏輯。
if (notSummer(date)) {
  charge = winterCharge(quantity);
}else {
  charge = summerCharge(quantity);
}



E. 以查詢取代臨時變數

問題: 正在使用臨時變數basePrice來儲存表示式的結果。
    double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000){
        return basePrice * 0.95;
    }
    else{
        return basePrice * 0.98;
    }

解決: 想辦法代替臨時變數,可用呼叫函數的方式。利用basePrice()當作條件式參數,呼叫函數計算_quantity*_itemPrice。
    if (basePrice() > 1000){
        return basePrice() * 0.95;
    }
    else{
        return basePrice() * 0.98;
    }
    ...
    double basePrice() {
        return _quantity * _itemPrice;
    }



F. 利用符號來替代魔法數字(科學常數、特定意義的數值等等)

問題: 將重力寫成9.81的數值。
 double potentialEnergy(double mass, double height) {
        return mass * 9.81 * height;
    }

解決: 將9.81改寫成GRAVITATIONAL_CONSTANT,再針對常數做定義,避免使用這種魔法數字。
 double potentialEnergy(double mass, double height) {
        return mass * GRAVITATIONAL_CONSTANT * height;
    }
    static final double GRAVITATIONAL_CONSTANT = 9.81;



-------------------------我是分隔線-------------------------

Q2: 舉出測試優先(Test-first development)的優點與潛在難處。

A2:

優點:

1. 避免設計難以測試的原件-因為必須先寫好測試程式,這會迫使我們去想元件實現的功能為何,同時可以避免當初寫好的程式碼不知道如何測試,造成必須要重新設計的窘境。

2. 階段性驗證程式-測試程式完成後,可以切割成很多個階段,透過每一段來驗證程式的正確性,可以確保程式在設計的過程中有階段性地達到功能。當發生測試錯誤時,可以退回上一個階段,重新檢視程式。

3. 不會捨棄測試程式-傳統開發流程會著重在需求與設計兩個主軸,有時候會因為時程開發有延遲,而捨棄一些測試的流程,或是直接跳過測試。但是測試導向的開發會先寫好測試程式,再進行設計。


潛在難處:

當我們不知道需求時,很難先寫好測試的程式,因為不知道元件最終會有什麼功能。對於實驗性或是需求不明的程式撰寫,必須先以需求與設計為主軸,才不會本末倒置。一般的資源配置會耗費較多時間在程式撰寫的階段,測試對於利害關係人而言只是驗證程式的一環,而不會願意耗費較多的時間在測試階段。

留言

手刀來看看