重構-改善既有程式碼的設計 讀後總結
阿新 • • 發佈:2019-01-24
重構-改善既有程式碼的設計,這本書是很多公司要求JAVA程式設計師必讀的三本書之一(另外兩本書是《Java程式設計思想》和《Effective
Java》)
前言
看到別人的程式碼時感覺就像屎一樣,有一種強烈的想重寫的衝動,但一定要壓制住這種衝動,你完全重寫,可能比原來的好一點,但浪費時間不說,還有可能引入原來不存在的Bug,而且,你不一定比原來設計得好,也許原來的設計考慮到了一些你沒考慮到的情況。我們寫的程式碼,終有一天也會被別人接手,很可能到時別人會有和我們現在一樣的衝動。所以,我們要做的是重構,從小範圍的重構開始
為啥要重構
何為重構:重構就是對軟體內部結構的一種調整,目的是不改變軟體可觀察行為的前提下,提高其可理解性,降低其可修改成本
通俗的定義:重構是優化程式碼結構,使其閱讀性更好,擴充套件性更強的一種高階技術
程式是首先寫給人看的,其次才是寫給機器看
重構函式
1. 重複程式碼
程式設計中不要有大量的重複程式碼,解決辦法就是去提煉到一個單獨的函式中
void A() { ..... System.out.println("name" + _name);}void B() { ..... System.out.println("name" + _name);}
更改為↓
void A() { .... }void B() { .... }void printName(String name) { System.out.println("name" + name);}
2. 內聯臨時變數
如果你對一個變數只引用了一次,那就不妨對他進行一次重構。
int basePrice = order.basePrice();return (basePrice > 100);
更改為↓
return (order.basePrice() > 1000);
3. 儘量去掉臨時變數
臨時變數多了會難以維護,所以儘量去掉所使用的臨時變數。
int area = _length * _width;if (area > 1000) return area * 5;else return area *4;
更改為↓
if (area() > 1000) return area() * 5;else return area() *4;int area() { return _length * _width;}
4. 引入解釋性變數
跟上面那個相反,如果使用函式變得很複雜,可以考慮使用解釋型變量了。
if ((platform.toUpperCase().indexOf("mac") > -1) && (brower.toUpperCase().indexOf("ie") > -1) && wasInitializes() && resize > 0) { ......
}
更改為↓
final boolean isMacOS = platform.toUpperCase().indexOf("mac") > -1;
final boolean isIEBrowser = brower.toUpperCase().indexOf("ie") > -1;
final boolean wasResized = resize > 0;
if (isMacOS && isIEBrowser && wasInitializes() && wasResized) { ...... }
5. 移除對引數的賦值
int discount (int inputVal, int quantity, int yearToDate) { if (inputVal > 50) inputVal -= 2;}
更改為↓
int discount (int inputVal, int quantity, int yearToDate) { int result = inputVal; if (result > 50) result -= 2;}
另外,函式中宣告的臨時變數最好只被賦值一次,如果超過一次就考慮再宣告變數對其進行分解了。
一個函式也不應該太長,如果太長首先影響理解,其次包含的步驟太多會影響函式複用。做法是將裡面的步驟提取為很多小函式,並且函式命名要體現出函式做了什麼,清晰明瞭。
重構類
1. 搬移方法
每一個方法應該放在她最適合的位置,不能隨便亂放,所以很多時候你需要考慮,一個方法在這裡是不是最適合的。
class Class1 { aMethod();}class Class2 {}
更改為↓
class Class1 {}class Class2 { aMethod();}
2. 搬移欄位
每一個欄位,變數都應該放到其自己屬於的類中,不能隨便放,不屬於這個類中的欄位也需要移走。
class Class1 { aField;}class Class2 {}
更改為↓
class Class1 {}class Class2 { aField;}
3. 提煉一個新類
將不屬於這個類中的欄位和方法提取到一個新的類中。所以說在你寫程式碼的時候一定要考慮這句話放這裡是不是合適,有沒有其他更合適的地方?
class Person {
private String name;
private String officeAreaCode; private String officeNumber;
public String getTelephoneNumber() { ..... } }
提煉到新的類中↓
class TelephoneNumber {
private String areaCode;
private String number;
public String getTelephoneNumber() { ..... } }
class Person {
private String name;
private TelephoneNumber _officeNumber; }
上面這種提煉類不一定就是合適的方式,有時候一個類不再有足夠的價值的時候,我們就需要考慮提煉類的反向操作了。將類變為內聯類了。
4. 內容移動
有時候每一個子類都有宣告一個欄位或方法,但是父類裡面卻沒有這個欄位或方法,這時候就考慮把這個欄位或方法移動到父類裡面,去除子類的這個欄位和方法。相反情況,如果父類有一個欄位或方法,但只是某個子類需要使用,就需要考慮吧這個欄位或方法移動到這個特定的子類裡面了。
5. 提煉介面
介面也就是協議,現在比較推崇的是面向介面程式設計。有時候介面將責任分離這個概念能發揮的淋漓盡致,把某些特性功能的方法提煉到介面中也是比較好的做法,這樣其他想要這種功能的類只需要實現這個介面就行了。
重新組織資料
1. 自封裝欄位
在一個類中訪問自己的欄位是不是應該把欄位封裝起來呢?這個每個人的觀點是不一樣的,把欄位封裝起來的好處就是:如果子類複寫這個欄位的getter函式,那麼可以在裡面改變這個欄位的獲取結果,這樣子擴充套件性可能會更好一點
private int _length. _width;public int area() { return _length * _width;}
更改為↓
private int _length. _width;
public int area() {
return getLength * getWidth();
}
int getLength() {
return _length;
}
int getWidth() {
return _width;
}
2. 以物件取代數值
隨著開發的進行,有時候一個數據項表示不再簡單了,比如剛開始只需要知道一個人的名字就行了,可是後來的需求變成了不但要知道這個人的名字還要知道這個人的電話號碼,還有住址等。這個時候就需要考慮將資料變成一個物件了。
class Order { private String name;}
更改為↓
class Order { private Person person;}class Person { private String name; private String tel; private String addr;}
我們有時候需要把Person寫成單利類,因為一個Person物件可以擁有很多份訂單,但是這個物件只能有一個,所以Person我們應該寫成單利。但有時候換成其他場景我們不能把他寫成單利。這都是要視情況而定的。隨意寫程式碼要小心謹慎。
3. 常量取代數字
有時候使用一個固定的數值並不是太好,最好使用建立一個常量,取一個有意思的名字來替換這個常量。
double circleArea(int redius) { return 3.14 * redius * redius}
更改為↓
double circleArea(int redius) { return pi * redius * redius;}public final double pi = 3.14;
簡化條件表示式
1. 分解條件表示式
有時候看著一個if else語句很複雜,我們就試著把他分解一下。我想不出好的例子了,就簡化一下了,各位莫怪。
if (isUp(case) || isLeft(case)) num = a * b;else num = a * c;
更改為↓
if (isTrue(case))
numberB(a);
}else
numberC(a);
boolean isTrue(case) {
return isUp(case) || isLeft(case);
}
int numberB(a) {
return a + b;
}
int numberC(a) {
return a + c;
}
2. 合併條件表示式
有時我們寫的多個if語句是可以合併到一起的。
double disabukutyAmount() { if (_seniority < 2) return 0; if (_monbtdiable > 12) return 0; if (_isPartyTime) retutn 0;}
更改為↓
double disablilityAmount() {
if (isNotEligibleForDisability())
return 0;
}
boolean isNotEligibleForDisability() {
return _seniority < 2 || _monbtdiable > 12 || _isPartyTime;
}
3. 合併重複的條件片段
有時候你可能會在if else 語句中寫重複的語句,這時候你需要將重複的語句抽出來。
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();
4. 以衛語句取代巢狀表示式
這個可能有點難以理解,但是我感覺用處還是比較大的,就是加入return語句去掉else語句
if (a > 0) result = a + b;else { if (b > 0) result = a + c; else { result = a + d; }}return result;
更改為↓
if (a > 0) return a + b;if (b > 0) return a + c;return a + d;
5. 以多型取代switch語句
這個我感覺很重要,用處非常多,以後你們寫程式碼的時候只要碰到switch語句就可以考慮能不能使用面向物件的多型來替代這個switch語句呢?
int getArea() { switch (_shap) case circle: return 3.14 * _r * _r; break; case rect; return _width + _heigth;}
更改為↓
class Shap {
int getArea(){};
}
class Circle extends Shap {
int getArea() {
return 3.14 * _r * _r; break;
}
}
class Rect extends Shap {
int getArea() {
return _width + _heigth;
}
}
然後在呼叫的時候只需要呼叫Shap的getArea()方法就行了,就可以去掉switch語句了。
然後我們還可以在一個方法中引入斷言,這樣可以保證函式呼叫的安全性,讓程式碼更加健壯。
簡化函式呼叫
首先要說明的是函式命名一定要有意思,一定要有意思,一定要有意思,重要的事情說三遍,不要隨便命名,命名一個函式或者方法的時候一定要能表明這個方法是幹什麼的。
將引數物件化
函式或方法最好不要有太多的引數,太長的引數難以理解,容易造成前後不一致,最好將引數物件化,傳入一個物件而不是幾個引數。
public void amountReceived(int start, int end);
該更為↓
public void amountReceived(DateRange range);
當然現在引數還是比較少,你可能看不出很多好處,但是一旦引數比較多的話,你就能看出好處了,將多個引數變成了一個引數。
總結 下面是我做的一些小注意點的筆記,在文章的末尾順便貼上一下,看看加深一下腦子的印象。
總結 下面是我做的一些小注意點的筆記,在文章的末尾順便貼上一下,看看加深一下腦子的印象。
- 當新增功能變得比較難的時候,就應該重構程式碼,先重構程式碼然後新增功能,重構程式碼應該一小步一小步的走。
- 方法要放到合適的類裡面,找到自己合適的位置
- 儘量去除多餘的臨時變數
- 把大方法分割為很多小方法,函式內容越小越容易管理。
- 儘量使用多型。
- 不要有過長的引數,和過大的類
- 重構時修改介面,要保留舊介面,並讓舊介面呼叫新介面。
- 出現switch就考慮使用多型來替換了。
- 儘可能的把大函式提煉成不同的小函式
- 有時候儘量使用行內函數
- 將一些臨時變數用函式代替
- 當if語句中的判斷表示式很多的時候,考慮使用臨時變數分解
- 臨時變數不應該賦值超過一次,應該使用final表示
- 移除對引數的改變,引數傳進函式中不應該被改變本身的值
- 有些難以提煉的函式可以考慮使用函式物件
- 程式碼儘量不要過多出現if else語句
文章摘自 http://www.jianshu.com/p/d6ff54d72afb
http://www.cnblogs.com/angeldevil/p/3601730.html
感謝兩位作者,學到很多東西