閱讀《重構》的一些思考

  終於在斷斷續續的狀況下把這本經典巨做看完了。算法

  這本書的全名叫作《重構-改善既有代碼的設計》,原有的代碼設計存在不足的地方讓人感到很差維護,因此才須要去改善既有代碼的設計,其實聽起來會不會有點亡羊補牢的感受?這裏也提醒了咱們一點:從設計代碼的初期就要深思熟慮,雖而後續的改動基本沒法避免,但良好的初期設計將對後續維護提供幫助。重構不是最終目的,咱們的目的是讓代碼變得更好。編程

關於書的內容組織

  不得不稱讚的一點是做者的寫做思路是很是清晰的。整本書的開始是由一個小型案例講起,讓讀者瞭解重構是一項什麼樣的工做,做者的案例設計也是挺巧妙的,值得不熟悉面向對象編程的讀者反覆揣摩。以後便開始講訴重構的一些概念,包括什麼是重構(在不改變軟件可觀察行爲的前提下,調整其內部結構,整理代碼的一個過程。注意與開發新功能的一個區分,開發新功能是在改變軟件可觀察行爲)、爲何要重構(重構能夠提升代碼的可維護性)以及何時重構(添加功能、修復錯誤、複審代碼的時候)。設計模式

  接下來就列舉了一系列要點講解很差的代碼設計的一些症狀。中間攜帶了關於測試體系的介紹,不過這一部分更多的是稍微提點一下,並無作比較深刻的分析。以後就迎來了整本書的重頭戲,關於重構手法的介紹,包括如何組織函數、在對象之間搬移特性(字段、函數)、從新組織數據、簡化條件表達式、簡化函數調用和處理繼承關係。隨後也順帶介紹了大型重構的四個手法,包括梳理並分解繼承體系、將過程化設計轉化爲對象設計、將領域與表述分離和提煉繼承體系。ide

  本書的最後是對於重構的一個歸納,做者認爲真正懂得重構的一個衡量標準是」你能夠自信地中止重構「,學會重構手法只是一個起點,把握什麼時候使用、什麼時候開始、什麼時候中止的節奏纔是使重構走向成功的關鍵。函數

閱讀重構手法的心得

  書中介紹了多種重構手法,這裏也不打算將其一一列舉出來,而是挑選了部分讓我醍醐灌頂的重構手法,以及閱讀中本身的一些思考。說實在話,本身在閱讀本書的時候並無作到很是細緻,特別是對於具體重構的步驟幾乎是一掃而過,讀得很是」粗「,可能跟我本身沒有真正地處理起大型的遺留代碼有關聯,思惟里老是迴響着一股這樣的聲音」這個重構手法是處理這樣的問題,恩,在最開始進行開發的時候就要留意這一點「,從而讓我以爲這些重構的具體步驟很繁瑣無趣,對於現階段也沒有較大的實用價值。或許等我真正接觸到很是多的遺留代碼問題,在來重讀本書會有新的體會。工具

  1. 把關好命名原則性能

  良好的命名是可讀代碼的基礎,書中對於這個概念也是很是重視的,甚至鼓勵開發者在沒有找到一個合適的命名前不要繼續往下開發。雖然書中並無討論如何把握命名方式,可是這裏我就班門弄斧一回,簡單地談一下本身的一些見解。測試

  首先要注意的第一個環節是要把握C#開發的基本命名規範,例如私有字段用‘_’開頭、函數名稱的首個單詞最好能是一個動詞、不要將變量名稱命名爲「temp」等,若是咱們能作好這最基本的一點,讓別人來看你的代碼也是那麼地清晰,這是否是一件美好的事情呢?因此,假如對於C#開發的基本命名規範尚未概念的話,是時候須要去惡補一下了。spa

  多數狀況下開發都離不開業務,因此良好的命名也要結合具體業務。在分析業務的同時也要積累業務相關的詞彙,例如Order表示訂單,Sku表示庫存量單位等,這些詞彙在命名的時候能夠提供支持。設計

  最後說起的一點策略是借鑑設計模式的詞彙。雖然有時候咱們不必定會在代碼中使用設計模式,可是借鑑某些詞彙來表達意圖也是能夠的,例如Factory、Adapter、Template、Bulider、Singleton、Proxy等,前人已經將經驗聚集提供給了咱們,咱們須要作的就是好好利用它們。

  整體而言,就連做者本人也認可的一個觀點是「經驗會帶來更多的幫助」,因此在開發中不只要重視名稱的選擇,也要注重經驗的積累。

  2. 使用return跳出多條件表達式

  說實在話,這是閱讀本書最讓我眼前一亮的重構手法了。不知道你們有沒有跟我有這樣的習慣:

public bool IsEnable()
{
    bool result = false;
    if (條件1)
    {
        result = true;
    }
    else
    {
        if (條件2)
        {
            result = true;
        }
        else
        {
            for (int i = 0; i < length; i++)
            {
                if (條件3)
                {
                    result = true;
                    break;
                }
            }
        }
    }
    return result;
}
多條件判斷代碼

  這裏用僞代碼模擬了一下場景,其實有可能實際的函數比這個還複雜,在函數的開頭就聲明一個result變量,接下來就是各類條件判斷對result賦值,最後在統一返回這個result,這就是開發人員對於「統一出口」的思想。其實有時候會不會以爲這樣的代碼看起來很繞?來看下使用這個手法重構後的代碼吧:

public bool IsEnable()
{
    if (條件1) return true;

    if (條件2) return true;

    for (int i = 0; i < length; i++)
    {
        if (條件3) return true;
    }
    return false;
}
重構後的代碼

  重構後帶來的最大變化是減小了else邏輯,及時地return避免引導讀者去看另一個沒有用的else區域,代碼變得更加清晰了。

  3. 沒有一成不變的規則,具體狀況具體分析

  重構手法更多地像一個工具箱,裏面放滿了十字螺絲刀,鉗子,扳手等,處理代碼遺留問題就像修理東西同樣,遇到不一樣的問題就要使用不一樣的工具。其實書中有時候做者介紹的重構手法是一個對立面,例如將提煉函數和內聯函數,一個主張提煉出一個獨立的函數,一個提倡不使用獨立的函數,因此涉及到具體狀況就要具體分析,要從理解重構手法的角度出發,沒有絕對的使用定律。

我所認爲的書中的不足之處

  有些討論的話題比較泛化,例如6.9節替換算法,「將函數本體替換爲另外一個算法」,內容描述起來比較空洞,有點相似於告訴咱們「嗯,這個函數這樣寫很差,換一種實現方式更好」,並不能合格地稱爲一種重構手法。

  對於某些重構手法我是保持着中立態度(意思是這種重構手法我不太喜歡這樣作),例如6.4節的「以查詢取代臨時變量」,來看一段書中給出的僞代碼:

//重構前的代碼
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
    return basePrice * 0.95;
else
    return basePrice * 0.85;

//重構後的代碼
if (CaculateBasePrice() > 1000)
    return CaculateBasePrice() * 0.95;
else
    return CaculateBasePrice() * 0.85;

double CaculateBasePrice()
{
    return _quantity * _itemPrice;
}
6.4節例子

  有時候爲了重用代碼,將表達式提煉到一個函數中,能夠更好地提升代碼複用率。但這個重構手法還有一個着重點:用提煉的函數去替代原來的變量。其實我以爲這裏用一個臨時變量也並無什麼問題,替換爲函數反而可能會出現一些問題:若是表達式是一個複雜的計算,每一個使用的地方都調用一遍會下降性能;在閱讀代碼的時候,若是遇到函數調用的狀況,都會將關注點調到函數中去,反而會下降了代碼可讀的流暢性。基於前面訴說的理由,我我的是不太喜歡實踐這個重構手法。

  不得不認可的一點,就是這本書的寫做時間是比較早的,以致於書中介紹的重構手法的實現步驟是創建於當時開發環境下的,部分作法能夠說是已經「過期」了。例如10.1節函數更名,書中介紹了較爲複雜的作法,但放在當今的開發環境下,IDE基本已經集成了函數更名這一重構功能,只要輸入新的函數名稱就能自動替換全部引用到舊函數的地方,已經大大提升了重構效率;6.1節提煉函數也是相似的狀況。

  不過,這並不阻礙這本書的歷史地位。推薦給各位讀者,讀完後也許你也會對代碼重構有新的感悟。

相關文章
相關標籤/搜索