一個軟件總是為解決某種特定的需求而產生,時代在發展,客戶的業務也在發生變化。有的需求相對穩定一些,有的需求變化的比較劇烈,還有的需求已經消失了,或者轉化成了別的需求。在這種情況下,軟件必須相應的改變。
考慮到成本和時間等因素,當然不是所有的需求變化都要在軟件系統中實現。但是總的說來,軟件要適應需求的變化,以保持自己的生命力。
這就產生了一種糟糕的現象:軟件產品最初制造出來,是經過精心的設計,具有良好架構的。但是隨著時間的發展、需求的變化,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以后,軟件的架構就千瘡百孔了。bug越來越多,越來越難維護,新的需求越來越難實現,軟件的架構對新的需求漸漸的失去支持能力,而是成為一種制約。最后新需求的開發成本會超過開發一個新的軟件的成本,這就是這個軟件系統的生命走到盡頭的時候。
重構就能夠最大限度的避免這樣一種現象。系統發展到一定階段后,使用重構的方式,不改變系統的外部功能,只對內部的結構進行重新的整理。通過重構,不斷的調整系統的結構,使系統對于需求的變更始終具有較強的適應能力。
重構可以降低項目的耦合度,使項目更加模塊化,有利于項目的開發效率和后期的維護。讓項目主框架突出鮮明,給人一種思路清晰,一目了然的感覺,其實重構是對框架的一種維護。
那么說到重構,可能我們普遍認為就是改改框架、使用一些最新流行的框架或者技術等等,我們到底要從哪些方面來對項目進行重構呢?下面我們進入主題:控制圈復雜度的9種重構技術
- 提煉函數
例子:如果某段代碼可以被組織在一起并獨立出來
void printOwing(double previousAmount)
{
Enumeration e = _orders.elements();
double outstanding = previousAmount * 1.2;
// 打印大標題
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
// 計算未完成的訂單數量
while (e.hasMoreElements())
{
Order each = (Order) e.nextElement();
outstanding += each.getAmount();
}
//打印明細
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
將這段代碼放進一個獨立函數中,并讓函數名稱解釋該函數的用途
void printOwing(double previousAmount)
{
printBanner();
double outstanding = getOutstanding(previousAmount * 1.2);
printDetails(outstanding);
}
void printBanner() //打印大標題
{
System.out.println ("**************************");
System.out.println ("***** Customer Owes ******");
System.out.println ("**************************");
}
// 計算未完成的訂單數量
double getOutstanding(double initialValue)
{
double result = initialValue;
Enumeration e = _orders.elements();
while (e.hasMoreElements())
{
Order each = (Order) e.nextElement();
result += each.getAmount();
}
return result;
}
void printDetails (double outstanding) //打印明細
{
System.out.println ("name:" + _name);
System.out.println ("amount" + outstanding);
}
- Substitute Algorithm(替換你的算法)
例子:
把當前算法重構成更清晰的算法
String foundPerson(String[] people)
{
for (int i = 0; i < people.length; i++)
{
if (people[i].equals ("Don"))
return "Don";
if (people[i].equals ("John"))
return "John";
if (people[i].equals ("Kent"))
return "Kent";
}
return "";
}
重構成更清晰的算法
String foundPerson(String[] people)
{
List candidates = Arrays.asList(new
String[]{"Don", "John","Kent"});
for (int i=0; i<people.length; i++)
if (candidates.contains(people[i]))
return people[i];
return "";
}
- Decompose Conditional(分解條件式)
例子:你有一個復雜的條件語句
if (date.before (SUMMER_START) || date.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharge;
else
charge = quantity * _summerRate;
從if、then、else三個段落中分別提煉出獨立函數
if (notSummer(date))
charge = winterCharge(quantity);
else
charge = summerCharge (quantity);
- Consolidate Conditional Expression(合并條件式)
例子:你有一系列條件判斷,都得到相同結果
double disabilityAmount()
{
if (_seniority < 2) return 0;
if (_monthsDisabled > 12) return 0;
if (_isPartTime) return 0;
// compute the disability amount
將這些判斷合并為一個條件式,并將這個條件式提煉成為一個獨立函數,函數名自注釋
double disabilityAmount()
{
if (isNotEligableForDisability()) return 0;
// compute the disability amount
- Consolidate Duplicate Conditional Fragments(合并重復的條件片斷)
例子:在條件式的每個分支上有著相同的一段代碼。
if (isSpecialDeal())
{
total = price * 0.95;
send();
}
else
{
total = price * 0.98;
send();
}
將這段重復代碼搬移到條件式之外,避免用拷貝粘貼的方式寫重復代碼
if (isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
- Remove Control Flag(移除控制標記)
例子:當前代碼使用標記變量,可讀性差,容易出錯
void checkSecurity(String[] people) {
boolean found = false;
for (int i = 0; i < people.length; i++) {
if (! found) {
if (people[i].equals ("Don")){
sendAlert();
found = true;
}
if (people[i].equals ("John")){
sendAlert();
found = true;
}
}
}
}
以break和return取代標記變量
void checkSecurity(String[] people) {
for (int i = 0; i < people.length; i++) {
if (people[i].equals ("Don")){
sendAlert();
break;
}
if (people[i].equals ("John")){
sendAlert();
break;
}
}
}
- Separate Query from Modifier(將查詢函數和修改函數分離)
例子:某個函數既返回對象狀態值,又修改對象狀態,建立兩個不同的函數,其中一個負責查詢,另一個負責修改
- Parameterize Method(令函數攜帶參數)
例子:若干函數做了類似的工作,但在函數本體中卻
包含了不同的值
Dollars baseCharge()
{
double result = Math.min(lastUsage(),100) * 0.03;
if (lastUsage() > 100) {
result += (Math.min (lastUsage(),200) - 100) * 0.05;
};
if (lastUsage() > 200) {
result += (lastUsage() - 200) * 0.07;
};
return new Dollars (result);
}
建立單一函數,以參數表達那些不同的值
Dollars baseCharge()
{
double result = usageInRange(0, 100) * 0.03;
result += usageInRange (100,200) * 0.05;
result += usageInRange (200, Integer.MAX_VALUE) * 0.07;
return new Dollars (result);
}
int usageInRange(int start, int end)
{
if (lastUsage() > start)
return Math.min(lastUsage(),end) -start;
else
return 0;
}