作者:江南白衣
 
 像工匠一樣進行重構, 讓重構成為一門手藝.
Martin Fowler的《Refactoring》其實更適合做一本關于重構的洗腦,宣言式的書,就像Kent Beck的《XP Explain》一樣薄薄的就可以了。只可惜他卻非常的厚,后面的重構名錄都是寫給小白看的。所以我更喜歡《Refacoring WorkBook》,以一個工匠的語氣(沉默寡言而實要)傳授重構的手藝。

1.重構 Between Classes
   〈Design pattern〉有半數篇幅教育大家不能只靠繼承,要善用組合/委托。重構里面其實也有很多事情靠把繼承變成委托來解決。
  1.1繼承
     1.1.1 并行繼承體系,組合爆炸
               這在以前是個頭痛的問題,現在都已習慣用委托。
               另外java還有個不是很讓人滿意的接口機制解決并行繼承。
     1.1.2  父子類的關系
                比如過于親密,子類會亂修改父類的方法,訪問父類的變量,這時候可以定義final的Template方法。
                還有拒絕的饋贈,我暫時還沒有在這上面遇到問題,作者也建議如果沒事就由他,如果有事,就要費勁的move method ;或者子類不繼承父類,而只是組合父類。
   1.2職責
     經過很多次重構之后,我發現,其實哪個方法應該放在哪個類其實很主觀的,你每天醒來都能想到一個理由讓一個方法搬一下家,所以我現在已經放棄追求一種“對”的職責分配了,看著順眼就行。
    
  1.3散彈式修改
     作一個修改就要改N個類時,也沒什么特別好方法,就是找找看,有沒有能為這個修改負責的統管全局的類。
     但現在的很多散彈式修改是分層做成的。

  1.4庫類
     OpenSource的類庫,總有些時候會想要擴展
     1.如果只是一兩個方法,直接在客戶代碼里擴展,
     2.否則自己多一個類庫的子類
     3.最費勁就是引入一個新的層
    
 題外話,重構其實很依賴工具,和對全部代碼的擁有度,嘩一下就來個全項目的rename。當你設計庫類時,你并不一定擁有使用這些庫類的客戶代碼了,因此一開始就要認真設計,不能依賴重構,改接口會讓人K死的。

2.重構 Within Classes
2.1 大是罪
   Long Method、Large Class、Long Parameter List, 一般通過度量工具找出來,還可以自己設定一個觸發器,當度量值超過某個限度時就報警。
   PMD可以實現這個功能,但度量工具我更喜歡Metrics Reload,一個IDEA的插件,給出的度量信息很全面。
   但是也切忌為了度量的數值而重構。
   Long Method當然是嘗試Extract Method。
   Large Class就要把類的職能分開幾個域,嘗試拆出另一個Class或者SubClass。
   Long Parameter List 可以通過在方法內的變量用查詢獲得而不用由參數傳入;
                                  或者把某些參數組成一個對象。
 
1.2 重復也是罪
    重復在30年前就被認為是不好的一樣東西,表現在1.代碼類似,2.代碼、接口不同而作用相近。
    去除重復的方法也沒什么特別,無非就是
    Extract Method(同一個類)。有差異時,通過參數化達到共用。
    Pull Up Method到父類(同一個父類)。有差異時,通過模板機制達到共用。
    Class A調用Class B   或者   Extract Class C  (兩個完全無相干的類)
 
1.3 命名
   中國程序員的英文本來就差,要多參考Ofbiz、Comperie的命名, 盡快建立團隊的項目字典、領域術語字典。
   也幸虧,現在在工具輔助下,代碼的rename是最容易的重構。

1.4復雜條件表達式

   作者認為,即使現在Program最關注的是對象,以及對象間的關系了,但優質的內部代碼依然重要,推薦《編程珠璣》和《Elements of Programing style》。

   化簡復雜條件的基本有三個方法
    1.通過!(A&B)==(!A)||(!B)化簡
    2.通過有意義的變量,函數代替條件表達式,比如
       boolean isMidScore = (X>1000)&&(X<3000);
    3.通過把一個if拆分開來執行,把guard clause放在前面
      比如if(A||B)
                  do();
            ->if(A)
                  do();
               if(B)
                  do();
          又可以把2、3靈活組合,比如根據2,Extract出一個isRight()函數,根據3
    isRight()
    {
      if(A)
        return true;
      if(B)
        return true;
      return false;
    }

1.5 其他
   沒用的死代碼,通過IDE工具探知并移除。小心有些框架代碼是不能刪除的。
   Magic Number,當然是改為常量。如果Magic Number很多,可以用Map、枚舉類來存放。
   除臭劑式的注釋,為方法,變量改一個更適合的名字。如果注釋是針對一個代碼段的,可以Extract Method。當然,代碼只能說明how, 不能說明why,更不能說明why not,這才是注釋存在的地方。