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

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

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

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

1.4復(fù)雜條件表達(dá)式

   作者認(rèn)為,即使現(xiàn)在Program最關(guān)注的是對象,以及對象間的關(guān)系了,但優(yōu)質(zhì)的內(nèi)部代碼依然重要,推薦《編程珠璣》和《Elements of Programing style》。

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

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