<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          代碼注釋的藝術(shù),優(yōu)秀代碼真的不需要注釋嗎?

          共 13710字,需瀏覽 28分鐘

           ·

          2022-06-20 21:57



          前言


          前天回家路上,有輛車(chē)強(qiáng)行插到前面的空位,司機(jī)大哥吐槽“加塞最可惡了”,我問(wèn)“還有更可惡的嗎”,司機(jī)大哥淡定說(shuō)道“不讓自己加塞的”。似乎和我們很類(lèi)似,我們程序員屆也有這2件相輔相成的事:最討厭別人不寫(xiě)注釋?zhuān)憛捵屪约簩?xiě)注釋。
          一段糟糕的代碼,往往大家最低的預(yù)期是把注釋寫(xiě)清楚,最合理的做法通常應(yīng)該對(duì)代碼做優(yōu)化。如果我們將代碼真正做到了優(yōu)秀,我們是否還需要注釋?zhuān)?/span>


          注釋的意義


          ; **************************************************************************; * RAMinit Release 2.0 *; * Copyright (c) 1989-1994 by Yellow Rose Software Co. *; * Written by Mr. Leijun *; * Press HotKey to remove all TSR program after this program *; **************************************************************************; Removed Softwares by RI:; SPDOS v6.0F, WPS v3.0F; Game Busters III, IV; NETX ( Novell 3.11 ); PC-CACHE; Norton Cache; Microsoft SmartDrv; SideKick 1.56A; MOUSE Driver; Crazy (Monochrome simulate CGA program); RAMBIOS v2.0; 386MAX Version 6.01

          注釋是對(duì)代碼的解釋和說(shuō)明,本質(zhì)目的是為了增強(qiáng)程序的可讀性與可解釋性。注釋會(huì)隨著源代碼,在進(jìn)入預(yù)處理器或編譯器處理后會(huì)被移除。這是雷布斯1994年寫(xiě)的一段MASM匯編代碼,注釋與代碼整體結(jié)構(gòu)都非常清晰。如果說(shuō)代碼是為了讓機(jī)器讀懂我們的指令,那注釋完全就是為了讓我們了解我們自己到底發(fā)出了哪些指令。

          爭(zhēng)議與分歧


          注釋的起源非常早,我們甚至已經(jīng)查閱不到注釋的由來(lái),但現(xiàn)在任何一種語(yǔ)言,甚至幾乎任何一種文本格式都支持各式各樣的注釋形式。
          但如何使用注釋?zhuān)鋵?shí)一直是一個(gè)備受爭(zhēng)論的話題。當(dāng)我們接手一段‘祖?zhèn)鞔a’時(shí),沒(méi)有注釋的感覺(jué)簡(jiǎn)直讓人抓狂,我們總是希望別人能提供更多的注釋。但軟件屆也有一段神話傳說(shuō),叫做『我的代碼像詩(shī)一樣優(yōu)雅』。有注釋的代碼都存在著一些瑕疵,認(rèn)為足夠完美的代碼是不需要注釋的。

          壞代碼的救命稻草


          The proper use of comments is to compensate for our failure to express ourself in code.
          -- Robert C. Martin 《Clean Code》
          譯:注釋的恰當(dāng)用法是彌補(bǔ)我們?cè)谟么a表達(dá)意圖時(shí)遭遇的失敗

          Clean Code 的作者Robert C. Martin可以說(shuō)是注釋的極力否定者了,他認(rèn)為注釋是一種失敗,當(dāng)我們無(wú)法找到不用注釋就能表達(dá)自我的方法時(shí),才會(huì)使用注釋?zhuān)魏我淮巫⑨尩氖褂茫覀兌紤?yīng)該意識(shí)到是自己表達(dá)能力上的失敗。
          PH&V的系統(tǒng)架構(gòu)師和負(fù)責(zé)人Peter Vogel,同樣也是一名堅(jiān)定的注釋否定著,他發(fā)表了一篇文章 why commenting code is still bad 來(lái)表述為代碼添加注釋在某種程度上可能是必要的,但確實(shí)沒(méi)有價(jià)值。
          事實(shí)上,我們也確實(shí)經(jīng)歷著非常多無(wú)價(jià)值的注釋?zhuān)约巴耆珣?yīng)由代碼來(lái)承擔(dān)解釋工作的“職能錯(cuò)位”的注釋。

          零注釋


          糟糕的代碼加上完全不存在的注釋?zhuān)蚁矚g稱(chēng)呼它們?yōu)椤何液蜕系壑g的秘密』,當(dāng)然過(guò)2個(gè)月后也可以稱(chēng)之為『上帝一個(gè)人的秘密』。
          壓垮程序員最后一根稻草的,往往都是零注釋??梢詻](méi)有文檔,可以沒(méi)有設(shè)計(jì),但如果沒(méi)有注釋?zhuān)覀兠恳淮伍喿x都是災(zāi)難性的。當(dāng)我們抱怨它一行注釋都沒(méi)有時(shí),其實(shí)我們是在抱怨我們很難理解代碼想要表達(dá)的含義,注釋是直接原因,但根本原因是代碼。
          零注釋往往和壞代碼一起生活,“沒(méi)有注釋”的吐槽,其實(shí)本質(zhì)上直擊的是那堆歪七扭八的英文字母,到底它們想表達(dá)什么!

          無(wú)用注釋

          /** * returns the last day of the month * @return the last day of the month */public Date getLastDayOfMonth(Date date) {    Calendar calendar = new GregorianCalendar();    calendar.setTime(date);    calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));    return calendar.getTime();}

          這是典型的廢話注釋?zhuān)x代碼時(shí)代碼本身就能很好的表達(dá)具體的含義,我們完全不需要看注釋?zhuān)⑶易⑨屢膊粫?huì)給我們提供更多有效的信息。無(wú)用注釋或許是零注釋的另一個(gè)極端,我們擔(dān)心自己寫(xiě)的代碼被人所吐槽,于是盡可能去補(bǔ)全注釋?zhuān)?dāng)你為 getLastDayOfMonth() 補(bǔ)一段 get last day of month 的注釋時(shí),恭喜你,你得到了雙倍的代碼。

          代碼優(yōu)于注釋

          "Comments Do Not Make Up for Bad Code"
          -- Robert C.Martin 《Clean Code》
          譯:注釋不能美化糟糕的代碼

          當(dāng)需要為一段代碼加上注釋時(shí),說(shuō)明代碼已經(jīng)不能很好的表達(dá)意圖,于是大家開(kāi)始為這段代碼添加注釋。Robert C.Martin在 Clean Code 中提出一個(gè)觀點(diǎn):注釋不能美化糟糕的代碼。能用代碼表達(dá)的直接用代碼表達(dá),不能用代碼表達(dá)的,你再想想,如何能用代碼表達(dá)。
          復(fù)雜的代碼最直接的表現(xiàn)就是不夠直觀、難以理解,加上注釋后往往會(huì)清晰很多,但你是愿意看這段代碼
          // 判斷是否活躍用戶if((customer.getLastLoginTime().after(dateUtils.minusDays(new Date(),15)) && customer.getCommentsLast30Days() > 5)     || orderService.countRecentDaysByCustomer(customer,30) > 1)

          還是這段代碼?
          if(customer.isActive())

          糟糕代碼的存在,通常是我們寫(xiě)注釋的常見(jiàn)動(dòng)機(jī)之一。這種試圖粉飾可讀性差的代碼的注釋稱(chēng)之為『拐杖式注釋』,即使大名鼎鼎的JDK,也存在這樣的拐杖式注釋。
          public synchronized void setFormatter(Formatter newFormatter) {    checkPermission();    // Check for a null pointer    newFormatter.getClass();    formatter = newFormatter;}

          這是取自JDK java.util.logging.Handler類(lèi)的setFormatter方法,作者為了不讓空指針異常下傳,提前做一次空指針檢查。沒(méi)有這段注釋我們完全不知道游離的這句 newFormatter.getClass() 到底要做什么,這段注釋也充分表達(dá)了作者自己也知道這句代碼難以理解,所以他加上了注釋進(jìn)行說(shuō)明。但我們完全可以用 Objects.requireNonNull() 來(lái)進(jìn)行替代。同樣的代碼作用,但可讀性可理解性大不一樣,JDK里的這段代碼,確實(shí)讓人遺憾。

          注釋否定論

          "If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much—perhaps not at all."
          -- Robert C.Martin 《Clean Code》
          譯:若編程語(yǔ)言足夠有表達(dá)力,或者我們長(zhǎng)于用這些語(yǔ)言來(lái)表達(dá)意圖,就不那么需要注釋--也許根本不需要

          通過(guò)代碼進(jìn)行闡述,是注釋否定論的核心思想。當(dāng)你花功夫來(lái)想如何寫(xiě)注釋?zhuān)屵@段代碼更好的表達(dá)含義時(shí),我們更應(yīng)該重構(gòu)它,通過(guò)代碼來(lái)解釋我們的意圖。每一次注釋的編寫(xiě),都是對(duì)我們代碼表達(dá)能力上的差評(píng),提升我們的歸納、表達(dá)、解釋能力,更優(yōu)于通過(guò)注釋來(lái)解決問(wèn)題。當(dāng)代碼足夠優(yōu)秀時(shí),注釋則是非必須的。并且需求在不斷調(diào)整,代碼一定會(huì)隨之變動(dòng),但注釋可能慢慢被人遺忘,當(dāng)代碼與注釋不匹配時(shí),將是更大的災(zāi)難。

          軟件設(shè)計(jì)的烏托邦


          曾經(jīng)我的確對(duì)優(yōu)秀的代碼不斷鉆研,對(duì)代碼本身所蘊(yùn)含的能量無(wú)比堅(jiān)信。如同當(dāng)科學(xué)代替鬼神論走上歷史舞臺(tái)時(shí),即使存在有科學(xué)解釋不了,我們依然堅(jiān)信只是科學(xué)還需要發(fā)展。當(dāng)代碼別人無(wú)法理解時(shí),我會(huì)認(rèn)為是我表述不夠精準(zhǔn),抽象不夠合理,然后去重構(gòu)去完善。
          有一次給老板review代碼,當(dāng)時(shí)老板提出,“你的代碼缺缺少注釋”,我說(shuō)不需要注釋?zhuān)a就能自解釋。于是老板現(xiàn)場(chǎng)讀了一段代碼,“query-customer-list 查詢(xún)客戶”、“transfer-customer-to-sales 分發(fā)客戶到銷(xiāo)售”、“check-sales-capacity 檢查銷(xiāo)售庫(kù)容”,每一個(gè)類(lèi)每一個(gè)函數(shù),一個(gè)單詞一個(gè)單詞往外蹦時(shí),你會(huì)發(fā)現(xiàn)好像確實(shí)都能讀懂,于是老板回了一個(gè)“好吧”。

          美麗的烏托邦

          "'good code is self-documenting' is a delicious myth"
          -- John Ousterhout《A Philosophy of Software Design》
          譯:‘好的代碼自解釋’是一個(gè)美麗的謊言

          在軟件設(shè)計(jì)中,總有一些軟件工程師所堅(jiān)信的詩(shī)和遠(yuǎn)方,有的是大洋彼岸的美好國(guó)度,有的或許是虛無(wú)縹緲的理想烏托邦。John Ousterhout教授在 A Philosophy of Software Design 中提到一個(gè)觀念,‘好的代碼自解釋’是一個(gè)美麗的謊言。
          我們可以通過(guò)選擇更好的變量名,更準(zhǔn)確的類(lèi)與方法,更合理的繼承與派生來(lái)減少注釋?zhuān)M快如此,我們還是有非常多的信息無(wú)法直接通過(guò)代碼來(lái)表達(dá)。這里的信息,或許不單單只是業(yè)務(wù)邏輯與技術(shù)設(shè)計(jì),可能還包括了我們的觀感,我們的體驗(yàn),我們的接納程度以及第一印象帶來(lái)的首因效應(yīng)。

          好代碼的最佳僚機(jī)


          You might think the purpose of commenting is to 'explain what the code does', but that is just a small part of it.The purpose of commenting is to help the reader know as much as the writer did.
          譯:你可能以為注釋的目的是“解釋代碼做了什么”,但這只是其中很小一部分,注釋的目的是盡量幫助讀者了解得和作者一樣多
          -- Dustin Boswell《The Art of Readable Code》

          如同John Ousterhout教授一樣,The Art of Readable Code 的作者Dustin Boswell,也是一個(gè)堅(jiān)定的注釋支持者。與Robert C.Martin類(lèi)似,Dustin Boswell同樣認(rèn)為我們不應(yīng)該為那些從代碼本身就能快速推斷的事實(shí)寫(xiě)注釋?zhuān)⑶宜卜磳?duì)拐杖式注釋?zhuān)⑨尣荒苊阑a。
          但Dustin Boswell認(rèn)為注釋的目的不僅解釋了代碼在做什么,甚至這只是一小部分,注釋最重要的目的是幫助讀者了解得和作者一樣多 。編寫(xiě)注釋時(shí),我們需要站在讀者的角度,去想想他們知道什么,這是注釋的核心。這里有非常多的空間是代碼很難闡述或無(wú)法闡述的,配上注釋的代碼并非就是糟糕的代碼,相反有些時(shí)候,注釋還是好代碼最棒的僚機(jī)。

          更精準(zhǔn)表述

          There are only two hard things in Computer Science: cache invalidation and naming things.
          -- Phil Karlton
          譯:計(jì)算機(jī)科學(xué)中只有兩個(gè)難題:緩存失效和命名 

          Martin Fowler在他的 TwoHardThings 文章中引用了Phil Karlton的一段話,命名一直都是一件非常難的事情,因?yàn)槲覀冃枰獙⑺泻x濃縮到幾個(gè)單詞中表達(dá)。很早之前學(xué)Java,接觸到很長(zhǎng)的類(lèi)名是ClassPathXmlApplicationContext??赡苡腥苏J(rèn)為只要能將含義準(zhǔn)確地表達(dá)出來(lái),名字長(zhǎng)一些無(wú)所謂。那如果我們需要有一段處理有關(guān)“一帶一路”的內(nèi)容,那我們的代碼可能是這樣的 
          public class TheSilkRoadEconomicBeltAndThe21stCenturyMaritimeSilkRoad {
          }

          他非常準(zhǔn)確的表達(dá)了含義,但很明顯這不是我們期望的代碼。但如果我們輔以簡(jiǎn)單的注釋?zhuān)a會(huì)非常清晰,說(shuō)明了簡(jiǎn)稱(chēng),也說(shuō)明了全意,表述更精準(zhǔn)。
          /** * 一帶一路 * 絲綢之路經(jīng)濟(jì)帶和21世紀(jì)海上絲綢之路 */public class OneBeltOneRoad {
          }




          代碼層次切割

          函數(shù)抽取是我們經(jīng)常使用且成本最低的重構(gòu)方法之一,但并非銀彈。函數(shù)并非抽得越細(xì)越好,如同分布式系統(tǒng)中,并非無(wú)限的堆機(jī)器讓每臺(tái)機(jī)器處理的數(shù)據(jù)越少,整體就會(huì)越快。過(guò)深的嵌套封裝,會(huì)加大我們的代碼閱讀成本,有時(shí)我們只需要有一定的層次與結(jié)構(gòu)幫助我們理解就夠了,盲目的抽取封裝是無(wú)意義的。
          /** * 客戶列表查詢(xún) */public List queryCustomerList(){    // 查詢(xún)參數(shù)準(zhǔn)備    UserInfo userInfo = context.getLoginContext().getUserInfo();    if(userInfo == null || StringUtils.isBlank(userInfo.getUserId())){        return Collections.emptyList();    }    LoginDTO loginDTO = userInfoConvertor.convertUserInfo2LoginDTO(userInfo);    // 查詢(xún)客戶信息    List<CustomerSearchVO> customerSearchList = customerRemoteQueryService.query(loginDTO);    Iterable<CustomerSearchVO> it = customerSearchList.iterator();    // 排除不合規(guī)客戶    while(it.hasNext()){        CustomerSearchVO customerSearchVO = it.next();         if(isInBlackList(customerSearchVO) || isLowQuality(customerSearchVO)){            it.remove();        }    }    // 補(bǔ)充客戶其他屬性信息    batchFillCustomerPositionInfo(customerSearchList);    batchFillCustomerAddressInfo(customerSearchList);}

          其實(shí)細(xì)看每一處代碼,都很容易讓人理解。但如果是一版沒(méi)有注釋的代碼,可能我們會(huì)有點(diǎn)頭疼。缺少結(jié)構(gòu)缺少分層,是讓我們大腦第一感觀覺(jué)得它很復(fù)雜,需要一次性消化多個(gè)內(nèi)容。通過(guò)注釋將代碼層次進(jìn)行切割,是一次抽象層次的劃分。同時(shí)也不建議大家不斷去抽象私有方法,這樣代碼會(huì)變得非常割裂,并且上下文的背景邏輯、參數(shù)的傳遞等等,都會(huì)帶來(lái)額外的麻煩。

          母語(yǔ)的力量

          其實(shí)上述例子,我們更易閱讀,還有一個(gè)重要的原因,那就是母語(yǔ)的力量。我們天然所經(jīng)歷的環(huán)境與我們每天所接觸到的事物,讓我們對(duì)中文與英文有完全不一樣的感受。我們代碼的編寫(xiě)本質(zhì)上是一個(gè)將我們溝通中的“中文問(wèn)題”,翻譯成“英文代碼”來(lái)實(shí)現(xiàn)的過(guò)程。而閱讀代碼的人在做得,是一件將“英文代碼”翻譯成“中文表述”的事情。而這之中經(jīng)過(guò)的環(huán)節(jié)越多,意思變味越嚴(yán)重。
          TaskDispatch taskDispatch = TaskDispatchBuilder.newBuilder().withExceptionIgnore().build();taskDispatch        // 外貿(mào)信息        .join(new FillForeignTradeInfoTask(targetCustomer, sourceInfo))        // 國(guó)民經(jīng)濟(jì)行業(yè)、電商平臺(tái)、注冊(cè)資本        .join(new FillCustOutterInfoTask(targetCustomer, sourceInfo))        // 客戶信息        .join(new FillCustomerOriginAndCategoryTask(targetCustomer, sourceInfo))        // 客戶擴(kuò)展信息        .join(new FillCustExtInfoTask(targetCustomer, sourceInfo))        // 收藏屏蔽信息        .join(new FillCollectStatusInfoTask(targetCustomer, sourceInfo, loginDTO()))        // 詳情頁(yè)跳轉(zhuǎn)需要的標(biāo)簽信息        .join(new FillTagInstanceTask(targetCustomer, sourceInfo, loginDTO()))        // 客戶信息完整度分?jǐn)?shù)        .join(new FillCustomerScoreTask(targetCustomer, sourceInfo))        // 潛客分層完整度        .join(new FillCustomerSegmentationTask(targetCustomer, sourceInfo))        // 填充操作信息        .join(new FillOperationStatusTask(targetCustomer, sourceInfo, loginDTO))        // 認(rèn)證狀態(tài)        .join(new FillAvStatusTask(targetCustomer, loginDTO))        // 客戶地址和組織        .join(new FillCompanyAddressTask(targetCustomer, loginDTO))        // 違規(guī)信息        .join(new FillPunishInfoTask(targetCustomer, sourceInfo))        // 填充客戶黑名單信息        .join(new FillCustomerBlackStatusTask(targetCustomer, sourceInfo))        // 填充客戶意愿度        .join(new FillCustIntentionLevelTask(targetCustomer, sourceInfo));        // 執(zhí)行        .execute();

          這是一段補(bǔ)齊客戶全數(shù)據(jù)信息的代碼,雖然每一個(gè)英文我們都看得懂,但我們永遠(yuǎn)只會(huì)第一眼去看注釋?zhuān)鸵驗(yàn)樗侵形?。并且也因?yàn)橛羞@些注釋?zhuān)@里非常復(fù)雜的業(yè)務(wù)邏輯,我們同樣可以非常清晰的了解到它做了哪些,分哪幾步,如果要優(yōu)化應(yīng)該如何處理。這里也建議大家寫(xiě)中文注釋?zhuān)⑨屖且环N說(shuō)明,越直觀越好。

          注釋的真正歸屬


          復(fù)雜的業(yè)務(wù)邏輯

          // Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {    throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {    // Not found -> check parent.    String nameToLookup = originalBeanName(name);    if (args != null) {        // Delegation to parent with explicit args.        return parentBeanFactory.getBean(nameToLookup, args);    }    else {        // No args -> delegate to standard getBean method.        return parentBeanFactory.getBean(nameToLookup, requiredType);    }}

          這是Spring中的一段獲取bean的代碼,spring作為容器管理,獲取bean的邏輯也非常復(fù)雜。對(duì)于復(fù)雜的業(yè)務(wù)場(chǎng)景,配上必要的注釋說(shuō)明,可以更好的理解相應(yīng)的業(yè)務(wù)場(chǎng)景與實(shí)現(xiàn)邏輯。
          截取自:
          org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean



          晦澀的算法公式

          /** * Returns the value obtained by reversing the order of the bits in the * two's complement binary representation of the specified {@code long} * value. */public static long reverse(long i) {    // HD, Figure 7-1    i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;    i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;    i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;    i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;    i = (i << 48) | ((i & 0xffff0000L) << 16) |        ((i >>> 16) & 0xffff0000L) | (i >>> 48);    return i;}

          這是JDK中Long類(lèi)中的一個(gè)方法,為reverse方法添加了足夠多的注釋。對(duì)于幾乎沒(méi)有改動(dòng)且使用頻繁的底層代碼,性能的優(yōu)先級(jí)會(huì)高于可讀性。在保證高效的同時(shí),注釋幫助我們彌補(bǔ)了可讀性的短板。
          截取自java.lang.Long#reverse

          不明所以的常量

          /** * The bin count threshold for using a tree rather than list for a * bin.  Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */static final int TREEIFY_THRESHOLD = 8;

          這是JDK中HashMap的一個(gè)常量因子,記錄由鏈表轉(zhuǎn)向紅黑樹(shù)的鏈表長(zhǎng)度閾值,超過(guò)該長(zhǎng)度則鏈表轉(zhuǎn)為紅黑樹(shù)。這里記錄了一個(gè)8,不僅記錄了該常量的用途,也記錄了為什么我們定義這個(gè)值。經(jīng)常我們會(huì)發(fā)現(xiàn)我們代碼中存在一個(gè)常量等于3、等于4,有時(shí)我們不知道這些3和4是干什么的,有時(shí)我們不知道為什么是3和4。
          截取自java.util.HashMap#TREEIFY_THRESHOLD

          意料之外的行為

          for (int i = 0; i < 3; i++) {    // if task running, invoke only check result ready or not    Result result = bigDataQueryService.queryBySQL(sql, token);    if (SUCCESS.equals(result.getStatus())) {        return result.getValue();    }    Thread.sleep(5000);}

          代碼及注釋所示為每5秒check一下是否有結(jié)果返回,遠(yuǎn)程服務(wù)將觸發(fā)與獲取放在了一個(gè)接口。沒(méi)有注釋我們可能認(rèn)為這段代碼有問(wèn)題,代碼表現(xiàn)的含義更像是每5秒調(diào)用一次,而非每5秒check一次。為意料之外的行為添加注釋?zhuān)梢詼p少對(duì)代碼的誤解讀,并向讀者說(shuō)明必要的背景及邏輯信息。

          接口對(duì)外API

          /** * <p>Checks if a CharSequence is empty (""), null or whitespace only.</p> * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.</p> * StringUtils.isBlank(null)      = true * StringUtils.isBlank("")        = true * StringUtils.isBlank(" ")       = true * StringUtils.isBlank("bob")     = false * StringUtils.isBlank("  bob  ") = false * * @param cs  the CharSequence to check, may be null * @return {@code true} if the CharSequence is null, empty or whitespace only */public static boolean isBlank(final CharSequence cs) {    final int strLen = length(cs);    if (strLen == 0) {        return true;    }    for (int i = 0; i < strLen; i++) {        if (!Character.isWhitespace(cs.charAt(i))) {            return false;        }    }    return true;}

          我們經(jīng)常使用的StringUtils工具類(lèi)中的isBlank方法,寫(xiě)了非常詳情的注釋?zhuān)粌H包括方法的邏輯,入?yún)⒌暮x,甚至還包括具體示例。我們平常定義的二方庫(kù)中的HSF、HTTP接口定義,同樣需要有清晰詳盡的注釋?zhuān)@里的注釋甚至經(jīng)常會(huì)多過(guò)你的代碼。
          截取自org.apache.commons.lang3.StringUtils#isBlank

          法律文件信息

          /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements.  See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License.  You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */

          與法律相關(guān)的注釋?zhuān)陂_(kāi)源軟件庫(kù)中較經(jīng)常遇到。涉及到一些版權(quán)及著作聲明時(shí),我們需要在源文件頂部放置法律相關(guān)注釋。當(dāng)然,我們不需要將所有法律信息寫(xiě)到注釋中,如例子中的跳鏈,引用一份標(biāo)準(zhǔn)的外部文檔,會(huì)是一個(gè)更好的選擇。

          寫(xiě)在最后


          注釋并不會(huì)妨礙你寫(xiě)出優(yōu)雅簡(jiǎn)潔的代碼,它只是程序固有的一部分而已。我們不用過(guò)分在意我們的代碼是否可以脫離注釋?zhuān)膊恍枰獜?qiáng)調(diào)因?yàn)槲覀兊拇a符合什么原則,滿足什么約定,所以代碼是優(yōu)秀的注釋是冗余的。代碼是一門(mén)藝術(shù),并不會(huì)因?yàn)闈M足三規(guī)九條它就一定完美,因?yàn)樗囆g(shù),是不可衡量的。
          參閱書(shū)籍
          《A Philosophy of Software Design》:https://www.amazon.com/-/zh/dp/173210221X/ref=sr_1_1
          《Clean Code》:https://baike.baidu.com/item/代碼整潔之道/9226259
          《The Art of Readable Code》:https://github.com/niexiaolong/niexiaolong.github.io/blob/master/the-art-of-readable-code.pdf

          推薦閱讀
          BigDecimal使用不當(dāng),造成P0事故!
          突然!VS Code 殺死 IDEA?!
          全球已超360萬(wàn)臺(tái)暴露在互聯(lián)網(wǎng)上
          僅花 2 小時(shí),網(wǎng)站就搭建好了,賊溜 ~

          →點(diǎn)關(guān)注,不迷路←



          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美操熟女 | 黄色视频在线免费看。 | 伊人婷婷大香蕉 | 丰满熟妇乱又伦 | 一本到高清无码 |