時隔一年,再議代碼提取


首先說兩件事情,我發現不同能力的人能掌控的代碼行數並不一樣,例如我,能掌控的一個函數長度也就在80行,而某些大牛能掌控的代碼行數是一個函數150~300行(不過大牛要求這個函數相對清晰,也就是說這個函數可以做多件事情,但是函數內每段代碼都只做一件事情,所以即使在函數內,代碼也是一塊一塊的,否則大牛也一樣覺得不爽),超過各自掌控的代碼行數,都會覺得難受

所以在能力的掌控范圍內,大牛並沒有強烈的提取代碼的需求,而我因為掌控能力不足所以即使在沒有明顯復用的情況下也有比較強烈的提取代碼的需求,所以我重構的一些程序,往往重構后代碼的長度並沒有明顯的減小(當然清晰程度是有所提升的)但是這樣有一個問題,就是當只有一個調用點時,有的時候我並不知道提取出多少代碼合適。

所以,這里可以為這篇文章定一個基調:提取代碼作為一個重構的重要手段是是必要也是必須的,但是不能濫用,沒有明顯復用的情況下,要能容忍大函數,並理解消化它。這也是通往大牛之路的必經之路。


 下面開始講如何提取代碼:一個函數應該只做一件事情,如果要提取,首先要識別代碼里的函數,到底做了什么,做了一件事還是多件事,如果做了多件事,才能考慮是不是要提取。看下面的函數

void doSomething()
{
    if(...)
    {
        // block1
    }

    for(...)
    {
        // block2
    }

    if(...)
    {
        // block3
    }
    else
    {
        // block4
    }
    
    // maybe more code
}

也就是說當看到包含大段大段代碼的多個控制結構出現在同一個函數內,這個函數就可能干了多件事情(當然,這樣也並非就一定不好,看文章看頭的內容),如果我們決定提取,那么怎么提取就是一個問題了,是否是每個控制結構就干了一件事情呢?也不一定,但假如一個控制結構就干了一件事情,那么是不是要連着控制結構一起提取呢?對於if/else結構來說比較好決定,因為這說明這件事情有不同的處理方式,要提取判斷條件一起提顯然還是合理的,但是單個if語句的情況就比較不好說了

if(...)
{
    // do something
}

這就回到了開頭所說的,如果這個代碼僅僅出於掌控能力不足提取出去的,那么應該是只有一個調用點,所以if本身是否需要提取實在是沒有參考依據,說不上好壞,但如果提取之后的代碼塊有多個調用點,那么情況就比較明顯了,除非每一個調用都必須進行判斷和迭代,它們才需要被提取,所以牛頭(我的一個大牛朋友)說,當只有一個調用點,看不出if需要遷出時,就保留在原地

那么如果if語句連着代碼塊一起提取,怎么表明代碼並不是所有時候都會執行if語句內的代碼呢?這個時候可以借助函數名來表達,例如

void removeNoteWhenMoreNote(...); // 即 do...When...的形式來命名

這是本篇文章最后一個環節:如何提高代碼的掌控能力,這才是本質問題。我后來發現什么樣的代碼我不易掌控?第一是變量泛濫的代碼,就是一個函數,不算函數參數至少得有10個局部變量,而且全部寫在了函數塊的頂部,讀這種代碼真是壓力山大啊,再有內部糾纏不清的代碼, so,代碼如果是這樣的應該就容易讀了:

void doSomething()
{
    // 做第一件事情
    if(...)
    {
        //...
    }

    //做第二件事情
    for(int i=0;i<SIZE;i++)
    {
        bool found = false;
        // ....
    }

    //做更多的事情
}
  • 變量定義在第一次使用前而不是堆在函數開頭
  • 變量應該盡量局部化,出了作用范圍,我應該能很放心的把他忘了
  • 做相同事情的代碼應該集中


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM