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

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

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

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

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

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

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

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