編輯:開發入門
偉大的創意少之又少,多數時候只是一些小改進。小的改進也是好的。
什麼是重構
可以運作的程序跟可以維護的程序之間,還有一道難以言說的鴻溝。
一個程序設計之初,是用來解決特定問題。就像在前面章節的學習中,我們也已經寫好了一個可以運作的 BMI 程序。但是對程序設計來說,當我們寫越多程序,我們會希望可以從這些程序之中,找到一個更廣泛適用的法則,讓每個程序都清晰易讀,從而變得更好修改與維護。
讓程序清晰易讀有什麼好處呢?當一段程序被寫出來,之後我們所要做的事,就是修改它與維護它。一旦程序越長越復雜,溷亂到無法維護的境界時,就只好砍掉重練。 所以若我們能透過某些方式,例如重新組織或部分改寫程序碼,好讓程序容易維護,那麽我們就可以為自己省下許多時間,以從容迎接新的挑戰。
我們回過頭來看看前面所寫的 Android 程序。android 平台的開發者已經先依照 MVC 模式,為我們將顯示界面所用的 XML 描述檔、顯示資源所用的 XML 描述檔從程序碼中區隔開來。將與程序流程無關的部份分開來組織,讓程序流程更清楚,相對易於維護。
而在主要程序碼(Bmi.Java) 方面,雖然程序碼量很少,還算好讀,但整體上並不那麽令人滿意。例如,假使我們要在這段程序碼中再多加上按鍵、適用於多種螢幕顯示模式、或是再加入選單等等內容,很快地程序碼就開始變得復雜,變得不容易閱讀,也開始越來越不容易維護。
因此,在繼續新的主題之前,我們先來重構這個 BMI 應用程序。在重構的過程中,也許我們能學到的東西,比學任何新主題還重要呢 。
MVC
我們打算重構 BMI 程序的部份 java 程序碼。既然我們已經照著 android 平台的作法,套用 MVC 模式在我們的程序組織上,那麽,我們不妨也試著套用同樣的 MVC 模式在 Bmi.Java 程序碼上。
如何套用 MVC 模式到 Bmi.Java 程序碼上呢?
原來的程序片段是這樣的
代碼 :
1 @Override
2 public void onCreate(Bundle icicle) {
3 super.onCreate(icicle);
4 setContentVIEw(R.layout.main);
5
6 //Listen for button clicks
7 Button button = (Button) findVIEwById(R.id.submit);
8 button.setOnClickListener(calcBMI);
9 }
上面的程序片段中,包含了所有 android 程序共用的標准內容, 整個程序的大致架構在前面章節中已經講解過,現在我們從中取出我們感興趣的部分來討論:
代碼:
Button button = (Button) findVIEwById(R.id.submit);
button.setOnClickListener(calcBMI);
在第7 行我們看到一段程序碼來宣告按鈕物件,與針對該按鈕物件作動作的程序碼。 button.setOnClickListener 程序碼的意義是指定一個函式,來負責處理" 按下" 這個" 按鈕" 後的動作。
我們可以想像,在同一個畫面中,多加入一些按鈕與欄位後,"onCreate" 這段程序將變得臃腫,我們來試著先對此稍作修改:
首先,我們可以套用 MVC 模式,將宣告界面元件( 按鈕、數字欄位) 、指定負責函式等動作抽取出來,將 onCreate 函式改寫如下
代碼:
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentVIEw(R.layout.main);
findVIEws();
setListensers();
}
接著我們將宣告界面元件的部份寫成一個獨立的"findVIEws" 函式:
private Button calcbutton;
private EditText fIEldheight;
private EditText fIEldweight;
private void findVIEws()
{
calcbutton = (Button) findVIEwById(R.id.submit);
fieldheight = (EditText) findVIEwById(R.id.height);
fieldweight = (EditText) findVIEwById(R.id.weight);
}
順便將原本很沒個性的按鈕識別參數"button" 改名成"calcbutton" ,以後在程序中一看到"calcbutton" ,就知道是一個按下後將開始處理計算工作的按鈕。
同樣地,我們也將指定特定動作( 按按鈕) 的負責函式獨立出來:
代碼:
//Listen for button clicks
private void setListensers() {
calcbutton.setOnClickListener(calcBMI);
}
如此一來,我們就將程序邏輯與界面元件的宣告分離開來,達成我們重構的目的。
完整程序如下:
代碼:
package com.demo.android.bmi;
import Java.text.DecimalFormat;
import android.app.Activity;
import android.os.Bundle;
import android.view.VIEw;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextVIEw;
public class Bmi extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentVIEw(R.layout.main);
findVIEws();
setListensers();
}
private Button button_calc;
private EditText fIEld_height;
private EditText fIEld_weight;
private TextView vIEw_result;
private TextView vIEw_suggest;
private void findVIEws()
{
button_calc = (Button) findVIEwById(R.id.submit);
field_height = (EditText) findVIEwById(R.id.height);
field_weight = (EditText) findVIEwById(R.id.weight);
view_result = (TextView) findVIEwById(R.id.result);
view_suggest = (TextView) findVIEwById(R.id.suggest);
}
//Listen for button clicks
private void setListensers() {
button_calc.setOnClickListener(calcBMI);
}
private Button.OnClickListener calcBMI = new Button.OnClickListener()
{
public void onClick(VIEw v)
{
DecimalFormat nf = new DecimalFormat("0.0");
double height = Double.parseDouble(fIEld_height.getText().toString())/100;
double weight = Double.parseDouble(fIEld_weight.getText().toString());
double BMI = weight / (height * height);
//Present result
vIEw_result.setText(getText(R.string.bmi_result) + nf.format(BMI));
//Give health advice
if(BMI>25){
vIEw_suggest.setText(R.string.advice_heavy);
}else if(BMI<20){
vIEw_suggest.setText(R.string.advice_light);
}else{
vIEw_suggest.setText(R.string.advice_average);
}
}
};
}
同樣是"calcBMI" 函式,在完整程序中,改將"calcBMI" 函式從原本的"OnClickListener" 宣告成 "Button.OnClickListener" 。這個改變有什麼差別呢?
閱讀原本的程序碼,在匯入(import) 的部分可以看到,"OnClickListener" 是來自於"android.view.VIEw.OnClickListener" 函式:
代碼:
import android.view.VIEw.OnClickListener;
改成"Button.OnClickListener" 後,"Button.OnClickListener" 就變成來自 於"android.widget.Button" 中的"OnClickListener" 函式,在查閱程序時,整個"Button" 與"OnClickListener" 之間的關係變得更清晰。
另外,我們偷偷將"OnClickListener" 中其他會存取到的界面元件識別參數,也補進 findVIEws 宣告中:
代碼:
private void findVIEws()
{
button_calc = (Button) findVIEwById(R.id.submit);
field_height = (EditText) findVIEwById(R.id.height);
field_weight = (EditText) findVIEwById(R.id.weight);
view_result = (TextView) findVIEwById(R.id.result);
view_suggest = (TextView) findVIEwById(R.id.suggest);
}
同時,我們也把識別參數的命名方法做了統一:按鈕的識別參數前加上 "button_" 前綴,可輸入欄位的識別參數前加上"field_" 前綴,用作顯示的識別參數前則加上"vIEw_" 前綴。將變數名稱的命名方法統一有 什麼好處呢?好處在於以後不管是在命名新變數,或是閱讀程序碼時,都能以更快速度命名或理解變數的意義,讓程序變得更好讀。
我們也把原本在程序中直接寫進的字串
代碼:
TextView result = (TextView) findVIEwById(R.id.result);
result.setText("Your BMI is "+nf.format(BMI));
改寫成
代碼:
//Present result
vIEw_result.setText(getText(R.string.bmi_result) + nf.format(BMI));
並將"TextView view_result" 宣告改放到 findVIEws 中一次處理好。
現在,整個程序流程是不是清爽了許多呢?
效果圖: Java代碼:import android.app.AlertDialog; import android.content.Context; import a
在android的源代碼中,經常會看到形如:sp<xxx>、wp<xxx>這樣的類型定義,這其實是android中的智能指針。智能指針是C++中
之所以有那麼多XXXTestCase主要是為了簡化工作。例如當你想對一個訪問數據庫的功能進行測試時,首先需要自己啟動並初始化數據庫。在 這裡是類似的,如果你想測試一
City-Go-RoundCity-Go-Round Web 站點在 2009 年 12 月在線推出(見 參考資料)。除了作為一個開放式交通數據 — 正如它所