代碼重構(英語:code refactoring)指對軟件代碼做一些改動以增加可讀性或者簡化代碼結構而不影響輸出結果。
軟件重構需要藉助重構工具完成,重構工具能夠修改代碼同時修改所有參照該代碼的地方。在極限程式設計的方法學中,重構需要單元測試來支援。
重構代碼
在軟件工程學裏,重構代碼一詞通常是指在不改變代碼的外部行為情況下而修改原始碼。在極限程式設計或其他敏捷方法學中,重構常常是軟件開發中的一部分:開發者輪流增加新的測試和功能,並重構代碼來增進代碼間的清晰性和一致性。同時,自動化的單元測試保證了重構後的代碼仍然能夠正常運作。
重構既不修正錯誤,也不增加新的功能。它的目的是用於提高代碼的可讀性或者改變代碼內部結構與設計或者刪除死碼,使其在將來更容易進行維護和開發。重構代碼可以存在於結構層面或是語意層面,但前提是不影響代碼在轉換前後的功能。一種進行重構的典型案例是:一個程式在現有的邏輯結構下難以加入新的功能,因此開發人員可能會對代碼進行重構,以便日後繼續開發。
一個重構的小範例是修改一個變數的名稱使其具有更明確的含義,例如從單個字母的「i」重構為「interestRate」(利率,圖一)。一個較複雜的重構的例子是把一段if區塊中的代碼變為一個子程式(圖二)。更複雜一點的重構是用多型性來替換if
條件式。進一步還可重新設計程式的演算法。在重構中最關鍵的是去有意地「清理」代碼,把不同的功能分開,然後對重構後的代碼進行測試(任何對代碼的編輯都有可能引發錯誤)。新的實現方法需要切合實際地改善現有設計,並且不改變原軟件的功能或行為。
難點
代碼重構也面臨諸多的挑戰。首先,對重構長遠的影響需要在重構後才能進行深入研究和追蹤。其次,重構某些底層業務邏輯或是資料庫架構幾乎是不可能的。最後,對介面造成影響的重構可能造成程式開發上的困境。例如,程式設計師若改變某介面中的方法名稱,除非他或她要對所有客戶端中連結到舊方法名的參考都加以編輯,否則就只能繼續維護使用舊方法名的介面,並另外建立舊名對新名的對映。
源流
首先使用「重構」一辭於出版文獻是於一篇文章:《Refactoring: An Aid in Designing Application Frameworks and Evolving Object-Oriented Systems, Proceedings of the Symposium on Object Oriented Programming Emphasizing Practical Applications (SOOPPA)》,1990年9月,由William F. Opdyke與Ralph E. Johnson聯名出版[1]。William Opdyke的博士論文於「重構:物件導向框架」,伊利諾大學,1992年出版[2]。「重構」術語幾乎至其後確定。
重構這個術語是從數字與多項式的因式分解類比而來[3]。如,x2 − 1可以被分解為(x + 1)(x − 1),這樣更好的整理並展示了式子的性質(例如式子擁有兩個根+1和−1)。同樣,在軟件重構中,結構上的改變通常會揭示原代碼中曾經「隱藏」的內部結構。
上面多項式的例子很好地展示了「重構」的思路。但是,一個多項式的不同表示形式並沒有優劣之分,它們展現了多項式的不同性質。在不同情況下適合的表達形式不同,並且還會隨着用戶的個人習慣或理解變化。這個問題於軟件開發領域亦然:個別程式員可能對某既定演算法的理想結構會有不同的意見。
重構方法簡單列表
下面是不完整的代碼重構清單。長一點的清單可以在福勒的重構書找到。因為研究者們繼續努力不懈的發明以及實現重構,完整清單可能永遠都不存在。
- 封裝成員變數(Encapsulate Field)—將僅限於本類別使用的變數重寫成私有(private)成員變數,並提供訪問方法(accessor method)。這種重構方式可以將與外部呼叫者無關的變數隱藏起來,減少代碼的耦合性,並減少意外出錯的概率。
- 提取方法(Extract Method)—意思是將大段代碼中的一部分提取後,構成一個新方法。這種重構可以使整段程式的結構變得更清晰,從而增加可讀性。這也對函數(Function)通用。
- 類一般化(Generalize Type)—將多個類別/函數共用的類型抽象出可以公用的基礎類別(base class),然後利用多型性追加每個類別/函數需要的特殊函數。這種重構可以讓結構更加清晰,同時可以增加代碼的可維護性。
- 函數歸父(Pull Up)—或譯函數上移,指的是方法從子類移動到父類別。
- 函數歸子(Push Down)—或譯函數下移,指的是方法從父類別移動到子類。
- 方法更名(Rename Method)—將方法名稱以更好的表達它的用途。
重構方法 | 重構前 | 重構後 |
---|---|---|
封裝成員變數 |
class SomeClass {
public int memberA;
...
}
|
class SomeClass {
private int memberA;
public int getMemberA();
public void setMemberA(int a);
...
}
|
方法提取 | void Process(MyDataSet mds)
{
// Step 1 ...
int result = 0;
if (mds.isReady)
{
int data1 = mds.param[0];
int data2 = mds.param[1];
// Preprocess...
result = data1 % data2;
}
// Step 2...
}
|
void Process(MyDataSet mds)
{
// Step 1 ...
int result = 0;
if (mds.isReady)
result = CalculateMDS(mds.param[0], mds.param[1]);
// Step 2 ...
}
int CalculateMDS(int data1, int data2)
{
// Preprocess...
return data1 % data2;
}
|
一般化類型 | class Rectangle {
private:
int w, h;
public:
double Area(){
return w*h;
}
}
class Triangle {
private:
int w, h;
public:
double Area(){
return w*h/2;
}
}
|
class Polygon {
private:
int w, h;
public:
virtual double Area() = 0;
}
class Rectangle : public Polygon {
double Area(){
return w*h;
}
}
class Triangle : public Polygon {
double Area() {
return w*h/2;
}
}
|
方法更名 | public double f(double m, double a);
|
public double calculateForce(double mass, double acceleration);
|
代碼重構自動化
許多軟件編輯器與整合環境支援重構自動化,又稱為重構瀏覽器。列舉如下:
- IntelliJ IDEA(Java專用)
- Eclipse給Java用的開發包(JDK)
- NetBeans(Java專用)
- Visual Studio 2005(.NET專用)
- CodeGear Delphi
- Bicycle Repair Man (頁面存檔備份,存於互聯網檔案館)(Python專用,可與emacs與vi共用)
- Refactoring Browser,Smalltalk專用
硬件重構
儘管代碼重構一詞最初只用於軟件代碼的重構,但近年來,硬件描述語言也出現了相應的代碼重構的概念。硬件重構(hardware refactoring)是對硬件描述語言中的代碼進行重構的簡稱。由於一般認為硬件描述語言與傳統程式語言有較大的區別[4],因此硬件重構被視為與傳統的代碼重構不同的領域。
研究者已經提出了模擬電路硬件描述語言(如VHDL-AMS)所使用的自動化重構方法[5]。根據他們的研究,硬件重構會在保持原本代碼的仿真結果的同時,增進行為模型的可理解性、可延伸性和可重用性,並使得後續自動化編譯過程的邏輯綜合步驟可以產生更加符合預期的結構表示。此外,Synopsys公司的Mike Keating也研究了手動進行的數字電路硬件描述語言的重構[6][7]。他所研究的目標是使複雜的硬件系統更易於理解,從而提高設計人員的生產力。
註釋
參考文獻
外部連結
參見
Wikiwand in your browser!
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.