如何寫出好代碼


如何寫出好代碼

這個題目把我自己都看傻了,因為仔細想想,這不是一個命題,是對代碼的思考,對細節的推敲和打磨。寫好代碼是一門學問,還是一種修行。
以前是公眾號(JackieZheng)和博客同步更新,尤其是技術類文章。但是最近在公眾號上寫的比較多,因為在那我可以想寫多少寫多少,隨時隨地記錄下自己的心得,還有勉勵自己的雞湯或是毒雞湯。
以后應該會階段性把公眾號的文章總結出來,寫成一篇博客,想了想,這樣比較符合這兩個平台的特性。

從《阿里官方Java代碼規范標准》說起

以前對於代碼規范的理解和積累都是瑣碎的,有時候從網上的文章看到如何命名,有時候從同行那里聽到如何進行代碼格式化。直到年前老大讓我看看《阿里官方Java代碼規范標准》,草草的過了遍,掠過某些點的時候有種被擊中的感覺(哎呀,這條規范我之前不是一直都在理所當然的違背么,恩,理所當然),有些點看了還是有些麻木,大概是因為還不知道正確的做法是什么。
一次被老大review code的過程中發現了自己的代碼中還是問題很多,最近也正好在看《代碼整潔之道》,這里結合過往深刻教訓以及公眾號的總結說說如何寫出好代碼。


老掉牙的命名

稍微接觸過一點點計算機的同學,都顯然知道,變量的命名不能以數字打頭,命名要有意義等等。
以前我認為只要不使用如i,j,k定義的變量名的工程師就是好的工程師,現在發現這是不夠的。

  • 魔法值
    魔法值,如果你看過《阿里官方Java代碼規范標准》,就肯定知道這個詞的意思——使用了沒有定義的值。比如maxPoint == 100,看着沒毛病啊,但是100這個值你這么用覺得合適么,人家還沒有定義過呢。如果有個這樣的聲明語句private static final int MAX_BOILING_POINT = 100,然后maxPoint == 100寫成maxPoint == MAX_BOILING_POINT,看着是不是要清晰不少,規范了許多。當然,類似的例子還有很多。
  • 1 l o 0
    你來認認看,他們都是誰;
    還有比如long id = 98765432l,這個剛剛有那么一瞬間把我自己都騙到了,最后一位不是1啊;
    我們知道對於doublelong類型,都需要在定義的之后面加上d``L,這是為了分別讓double類型和float類型以及intlong區分開來。但是就像《阿里官方Java代碼規范標准》中強調的那樣,對於long類型我們應該使用L,應為如果寫成long id = 5432l就會出現上面一樣的問題;
    諸如此類的小細節都是坑,要么坑自己,要么坑隊友。
  • 命名有意義
    i j k這套確實看着讓人頭暈,那type data string這套呢,看着好像好了不少,其實呢,還是換湯不換葯,你能知道這type data string分別代表什么意思么,那再換換salaryLevel totalSalary employeeName,現在好多了,雖然變量長度增加了但是這樣能夠保證以后的你或者別人在閱讀代碼的時候能夠讀到那就明白到哪,而不是一遍又一遍的查看這個變量是怎么傳過來的,源頭的含義是什么。

函數

函數貴在短小。

  • 當你讀代碼,看到一個方法,如果長到需要滾動鼠標來往下翻后續內容,再碰上沒有區塊或方法層注釋的,就要抓狂了。這時候短小的優勢就出來了,通過區區幾行就能很好詮釋你的函數干了什么事豈不是皆大歡喜。
    如果能通過閱讀方法名就知道這個方法是干什么的最好了。比如現在有方法叫做getData(...),還有一個叫做getPersonByName(...),顯然后者強勢碾壓前者,起碼我是這么認為的。這么寫還有一個附加好處,就是省去了注釋,因為方法名就是注釋。
    函數的目的就是為了告訴你它干了什么事,准確的說,它干了具體哪一件事。在設計模式的指導思想里面是有單一職責原則,對於函數也是一樣。別指望一個函數能把所有的活都包了,那樣大家都很累。比如
public PersonResult doSomething(String name, boolean isMarked, Person person) {
    String[] names = name.split(",");    
    for(String name : names) {        
        //do some convert or others operations    
    }    
    if(isMarked) {
        PersonResult  personResult = new PersonResult();
        personResult.setAge(person.getAget());        
        // ...
    }    
    return personResult;
}

例子是剛剛想的,當然你可以寫一個很長的全能doSomething函數,但是讀的人真的累。如果可以的話,這樣是不是更好點

稍稍對比下,短小真的不是壞事,它能保持函數的內容在同一抽象層上,看着也舒服。

  • 同一抽象層。這個概念很有意思,請教了老大有了比較好的理解。比如
public double getTotalSalaray() {
    double baseSalaray = getBaseSalary();

    //...
    
    double salaryPart1 = 100;
    double salaryPart2 = 200;
    double salaryPart3 = 300;
    double performanceSalary = salaryPart1 + salaryPart2 + ... + salaryPartN;N
    
    return baseSalary + performanceSalary;
}

這個函數很單純,真的只做了一件事,就是得到工資總額,包括基本工資和績效工資。當我們看完第一行代碼的時候,我們覺得賞心悅目,很好理解,如果你比較關系基本工資的細節,大可進入getBaseSalary()方面里面一看究竟,可是在后面的績效工資部分,就顯得有些畫風不對,我們不需要着這樣的細節處理,我們更渴望與獲取基本工資一樣的抽象即可,好比這樣

public double getTotalSalaray() {
    double baseSalaray = getBaseSalary();

    //...
    
   double performanceSalaray = getPerformanceSalary();
    return baseSalary + performanceSalary;
}

注釋

有多少碼農曾經一直被告誡要多些注釋,寫好注釋,起碼我之前是這樣的被叮囑的。但是看完《代碼整潔之道》,發現注釋不是什么好東西,起碼沒有我們想的那么好。

  • 注釋的副作用
    誰也沒法保證能一氣呵成寫完代碼,而且永遠不會有變動,所以我們需要經常變動代碼,但是絕大多數時候,我們卻由於各種原因忽視了變動注釋。時間久了,代碼和注釋就不配套了,容易產生誤解讀。
    在寫下詳細代碼之后,又開始為這塊代碼加上注釋,但是或許因為腦瓜子缺氧或是開小差了,寫的注釋與代碼表達的邏輯不符,這樣會讓讀者抓耳撓腮,匪夷所思。比如
public void getData(boolean isAdmin) {
    // if isAdmin is true, get isGroup, if isGroup is true then doSomething
    if(!isAdmin) {
        boolean isGroup = PermissionUtil.getIsGroup();
        if(isGroup) {
            ...
         }
    }
}

因為有很多判斷,相信你應該會看一眼注釋來幫助自己更好的理解這段代碼,但是仔細一推敲,發現代碼中的邏輯應該是isAdminfalse的時候,才會去獲取isGroup的值,而非像注釋中提到為true的時候。

  • 有時候我們需要注釋
    必要的Javadoc,好比
/**
*get Person entity by id
*/
...

幫助澄清信息,比如

//example: www.test.childtest.com/v5/666
...domain + childDomain + version + id...

警示信息,對於一些代碼的使用有些特殊場景要求,可以通過注釋標示出來。
TODO 這個大家應該會經常使用吧,用來標記一些我們即將要實現的功能或者帶擴展的功能。

今天就到這,后面有機會再整理。
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。


免責聲明!

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



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