當我們對代碼理解不夠深刻時,往往會感覺 重構改善既有代碼的設計 這本書的內容過於浮誇,頗有“初聞不知曲中意,再聞已是曲中人”之感.
但根據個人經驗,還是希望能儘早地運用其中的一些技巧。
該篇文章的內容主要來自於書中的第6章——重新組織函數
Linux 的創始人 Linus Torvalds 曾說:Talk is cheap.Show me the code
下面我將書中的一些技巧總結一下,以供參考學習交流。
1. 提煉函數(Extract Method)
將某段代碼放進一個獨立的函數中,並讓函數名稱解釋該函數的用途。
無論你是否已經意識到,下面這幾點你都是無可反駁的事實:
- 如果每個函數的粒度都很小,則函數被複用的機率就更大;
- 代碼的閱讀性更高;
- 覆寫函數會變得更容易;
人們都會有疑問,函數的長度多少纔算合適?
書中強調,函數的關鍵不在於長度,而在於函數名稱能否正確表達出函數的功能;如果提煉可以強化代碼的清晰度,那就儘管去做,就算函數名稱比提煉出來的代碼還長也無所謂。
這裏舉個例子,基礎好的朋友也許一眼就能看出來這是個冒泡排序並打印排序後的數據。很顯然,並不是每一個人都能直接讀懂的。這裏我們一定要明白,代碼很多時候是給其他人看的。
我們就按照提煉函數的思路來優化一下
public static void main(String[] args) {
int[] array = {1,5,3,2};
for (int i = 0; i < array.length-1; i++) {
for (int j = 0; j < array.length-1-i; j++) {
if(array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
Arrays.stream(array).forEach(System.out::println);
}
優化版本1:
// 冒泡排序
public static void bubbleSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j] > arr[j+1]){
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
// 再看main函數,是不是感覺很輕鬆
public static void main(String[] args) {
int[] array = {1,5,3,2};
bubbleSort(array);
// 這裏,採用Java 1.8 的寫法其實是爲了故弄玄虛的,這樣我們提煉函數纔有意義
Arrays.stream(array).forEach(System.out::println);
}
經常刷算法的朋友對上面的冒泡排序或許會認爲還有優化空間,這就產生了我們的
優化版本2:
public static void BubbleSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if(arr[j] > arr[j+1]){
swap(arr,j,j+1);
}
}
}
}
// 互換操作,使用的場景還是挺多的,這裏更多的是考慮到複用場景
public static void swap(int[] nums,int i,int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
優化版本3:
// 再看main函數,是不是感覺很輕鬆
public static void main(String[] args) {
int[] array = {1,5,3,2};
bubbleSort(array);
printArray(array);
}
// 打印數組,更多地從可讀性上來考慮
public static void printArray(int[] array){
Arrays.stream(array).forEach(System.out::println);
}
經多上面的一系列優化重構操作,main函數是不是讓你一目瞭然。
當然在我們實際的開發過程中,可能需要考慮到很多場景——入參 和 出參 ,尤其是當其數量過多時,我們有可能考慮使用多個函數or對象包裹等方法,這裏就不一一展開,更多地內容,可以去看書籍,不過還是想強調一點:
紙上得來終覺淺,絕知此事要躬行。
2. 內聯函數(Inline Method)
// 這個是JDK自帶的函數,但是在實際開發中使用的很少。
public boolean isEmpty() {
return size == 0;
}
// 233333... 一般在開發中沒這麼寫的吧,這裏就需要 **內聯** 一下
public static boolean isAvailable(List list){
return (list == null || list.isEmpty()) ? false : true;
}
內聯後的效果,也就是大家開發中最常用的:
public static boolean isAvailable(List list){
return (list == null || list.size() ==0 ) ? false : true;
}
這裏的例子可能不是太恰當,But不要懵逼…,其實就是函數不必要就刪掉
3. 內聯臨時變量(Inline Temp)
public static boolean isAvailable(List list){
// 在此處,size臨時變量就該被內聯化
int size = list.size();
return (list == null || size != 0 ) ? false : true;
}
書中說了那麼多,理解起來就是一句話:不要多此一舉
綜合2,3來看,
內聯 即 Don’t 多此一舉
大家更關係,如何識別什麼是內聯變量 ?
只被一個簡單表達式賦值一次的變量。
書中的這個技巧可以學一下:
若該變量沒有被聲明爲final,可將聲明爲final,然後編譯,這樣可以確定該臨時變量是否真的只被賦值一次
這個章節的內容過多,其它的放在後續文章中講解,歡迎掌眼…