日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

CodeReview 常見(jiàn)代碼問(wèn)題( 下 )

 xujin3 2018-08-18

相關(guān)閱讀:

優(yōu)秀架構(gòu)師必須掌握的架構(gòu)思維

CodeReview 常見(jiàn)代碼問(wèn)題( 上 )

互聯(lián)網(wǎng)技術(shù)(java框架、分布式、集群)干貨視頻大全,不看后悔!(免費(fèi)下載)

來(lái)源:琴水玉 ,

www.cnblogs.com/lovesqcc/p/9271781.html


可維護(hù)性問(wèn)題


可維護(hù)性問(wèn)題是“在當(dāng)前業(yè)務(wù)變更的范圍內(nèi)通常不會(huì)導(dǎo)致BUG、故障,卻會(huì)在日后埋下地雷,引發(fā)BUG、故障、維護(hù)成本大幅增加”的類別。


硬編碼


硬編碼主要有三種情況: a. “魔數(shù)”; b. 寫死的配置; c. 臨時(shí)加的邏輯和文案。


“魔數(shù)”與重復(fù)代碼類似,當(dāng)前或許不會(huì)引發(fā)問(wèn)題,時(shí)間一長(zhǎng),為了弄清楚其代表的含義,增加很多溝通維護(hù)成本,且分散在各處很容易導(dǎo)致修改的時(shí)候遺漏不一致。務(wù)必清清除。方法也比較簡(jiǎn)單:定義含義明顯的枚舉或常量,代表這個(gè)魔數(shù)在代碼中發(fā)言。


“寫死的配置”不會(huì)影響業(yè)務(wù)功能, 不過(guò)在環(huán)境變更或系統(tǒng)調(diào)優(yōu)的時(shí)候,就顯得很不方便了。 方法: 盡量將配置抽離出來(lái)做成配置項(xiàng)放到配置文件里。


“臨時(shí)加的邏輯和文案”也是一種破壞系統(tǒng)可維護(hù)性的做法。方法: 抽離出來(lái)放在單獨(dú)的函數(shù)或方法里,并特別加以注釋。


重復(fù)代碼


重復(fù)代碼在當(dāng)前可能不會(huì)造成 BUG,但上線后,需要維護(hù)多處的事實(shí)一致性;時(shí)間一長(zhǎng),后續(xù)修改的時(shí)候就特別容易遺漏或處理不一致導(dǎo)致 BUG;重復(fù)代碼是公認(rèn)的“代碼壞味”,必當(dāng)盡力清除。方法: 抽離通用的部分,定制差異。重復(fù)代碼還有一種情況出現(xiàn),即創(chuàng)造新函數(shù)時(shí),先看看是否既有方法已經(jīng)實(shí)現(xiàn)過(guò)。


通用邏輯與定制業(yè)務(wù)邏輯耦合


這大概是每個(gè)媛猿們?cè)陂_(kāi)發(fā)生涯中遇到的最惡心的事情之一了。通用邏輯與具體的各種業(yè)務(wù)邏輯混雜交錯(cuò),想插根針都難。遇到這種情況,只能先祈福,然后抽離一個(gè)新的函數(shù),嚴(yán)格判斷相應(yīng)條件滿足后去調(diào)用它。

如果是新創(chuàng)建邏輯,可以使用函數(shù)式編程或基于接口的編程,將通用處理流程抽離出來(lái),而將具體業(yè)務(wù)邏輯以回調(diào)函數(shù)的形式傳入處理。


不要讓不同的業(yè)務(wù)共用相同的函數(shù),然后在函數(shù)里一堆 if-else plus switch , 而是每個(gè)業(yè)務(wù)都有各自的函數(shù), 并可復(fù)用相同的通用邏輯和流程處理; 或者各個(gè)業(yè)務(wù)可以覆寫同樣命名的函數(shù)。


復(fù)用,而非混雜。


直接在原方法里加邏輯


有業(yè)務(wù)改動(dòng)時(shí),猿媛們圖方便傾向于直接在原方法里加判斷和邏輯。這樣做是很不好的習(xí)慣。一方面,增加了原方法的長(zhǎng)度,破壞了其可維護(hù)性;另一方面,有可能對(duì)原方法的既有邏輯造成破壞。 可靠的方式是: 新增一個(gè)函數(shù),然后在原方法中調(diào)用并說(shuō)明原因。


多業(yè)務(wù)耦合


在業(yè)務(wù)邊界未仔細(xì)劃分清晰的情況下出現(xiàn),一個(gè)業(yè)務(wù)過(guò)多深入和摻雜另一個(gè)非相關(guān)業(yè)務(wù)的實(shí)現(xiàn)細(xì)節(jié)。在項(xiàng)目和系統(tǒng)設(shè)計(jì)之初,特別要注意先劃分業(yè)務(wù)邊界,定義好接口設(shè)計(jì)和服務(wù)依賴關(guān)系,再著手開(kāi)發(fā);否則,延遲到后期做這些工作,很可能會(huì)導(dǎo)致重復(fù)的工作量,含糊復(fù)雜的交互、增加后期系統(tǒng)維護(hù)和問(wèn)題排查的許多成本。磨刀不誤砍柴工。劃分清晰的業(yè)務(wù)、服務(wù)、接口邊界就屬于磨刀的功夫。


代碼層次不合理


代碼改動(dòng)邏輯是正確的,然而代碼的放置位置不符合當(dāng)前架構(gòu)設(shè)計(jì)約定,導(dǎo)致后續(xù)維護(hù)成本增加。


代碼層次不合理可能導(dǎo)致重復(fù)代碼。比如獲取操作人和操作記錄,如果寫在類 XController 里, 那么類 YController 就面臨尷尬局面: 如果寫在 YController , 就會(huì)導(dǎo)致重復(fù)代碼; 如果跨層去調(diào)用 XController 方法,又是非常不推薦的做法。因此, 獲取操作人和操作記錄,最好寫在 Service 層, Controller 層只負(fù)責(zé)參數(shù)傳入、檢測(cè)和結(jié)果轉(zhuǎn)譯、返回。


不用多余的代碼


工程中常常會(huì)有一些不用的代碼?;蛘呤且恍簳r(shí)未用到的Util工具或庫(kù)函數(shù),或者是由于業(yè)務(wù)變更導(dǎo)致已經(jīng)廢棄不用的代碼,或者是由于一時(shí)寫出后來(lái)又重寫的代碼。盡量清除掉不用多余的代碼,對(duì)系統(tǒng)可維護(hù)性是一種很好的改善,同時(shí)也有利于CodeReview。


使用全局變量


使用全局變量并沒(méi)有“錯(cuò)”,錯(cuò)的是,一旦出現(xiàn)問(wèn)題,排查和調(diào)試問(wèn)題起來(lái),真的會(huì)讓人“一夜之間白了頭”,耗費(fèi)數(shù)個(gè)小時(shí)是輕微懲罰。此外,全局變量還能“順手牽羊”地破壞函數(shù)的通用性,導(dǎo)致可維護(hù)性變差。務(wù)必消除全局變量的使用。當(dāng)然,全局常量是可以的。


缺乏必要的注釋


對(duì)重要和關(guān)鍵點(diǎn)的代碼缺乏必要的注釋,使用到的重要算法缺乏必要的引用出處,對(duì)特別的處理缺乏必要的說(shuō)明。


原則上, 每個(gè)方法至少要用一個(gè)簡(jiǎn)短的單行注釋, 適宜地描述了方法的用途、業(yè)務(wù)邏輯、作者及日期。對(duì)于特殊甚至奇葩的需求的特別實(shí)現(xiàn),要加一些注釋。 這樣后續(xù)維護(hù)時(shí)有個(gè)基礎(chǔ)。


更難發(fā)現(xiàn)的錯(cuò)誤


更難發(fā)現(xiàn)的錯(cuò)誤是指“復(fù)雜并發(fā)場(chǎng)景下的有一定技術(shù)難度的、需要豐富開(kāi)發(fā)與設(shè)計(jì)經(jīng)驗(yàn)才能看出來(lái)的錯(cuò)誤”。


并發(fā)


并發(fā)的問(wèn)題更難檢測(cè)、復(fù)現(xiàn)和調(diào)試。常見(jiàn)的問(wèn)題有:a. 在可能由多線程并發(fā)訪問(wèn)的對(duì)象中含有共享變量卻沒(méi)有同步保護(hù);b. 在代碼中手動(dòng)創(chuàng)建缺乏控制的線程或線程池;c. 并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí)沒(méi)有做任何同步措施;d. 多個(gè)線程對(duì)同一對(duì)象的互斥操作沒(méi)有同步保護(hù)。


對(duì)于 a, 在大部分Java應(yīng)用中,通常由Spring框架來(lái)控制和創(chuàng)建請(qǐng)求和服務(wù)實(shí)例,因此,保證“Controller, Service 類中的實(shí)例變量只允許 Service, DAO 的單例,不允許業(yè)務(wù)變量實(shí)例”基本確保沒(méi)有并發(fā)不正確更新的問(wèn)題;不過(guò),包含緩存策略的對(duì)象要特別注意多線程并發(fā)訪問(wèn)的問(wèn)題,出于性能考量, 盡量只對(duì)共享實(shí)例部分加鎖。


對(duì)于 b, 禁止在應(yīng)用中手動(dòng)創(chuàng)建線程或線程池,失控的線程池很容易導(dǎo)致應(yīng)用崩潰(有線上應(yīng)用崩潰的教訓(xùn))。


對(duì)于 c, 并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),要特別注意時(shí)序和狀態(tài)同步。如果時(shí)序控制不對(duì),會(huì)導(dǎo)致?tīng)顟B(tài)同步和更新出錯(cuò)。


對(duì)于 d, 對(duì)同一對(duì)象的互斥操作需要加分布式鎖同步。


使用線程池、并發(fā)庫(kù)、并發(fā)類、同步工具而不是線程對(duì)象、并發(fā)原語(yǔ)。在復(fù)雜并發(fā)場(chǎng)景下,還需注意多個(gè)同步對(duì)象上的鎖是否按合適的順序獲得和釋放以避免死鎖,相應(yīng)的錯(cuò)誤處理代碼是否合理。


事務(wù)


事務(wù)方面常出現(xiàn)的問(wèn)題是:多個(gè)緊密關(guān)聯(lián)的業(yè)務(wù)操作和 SQL 語(yǔ)句沒(méi)有事務(wù)保證。 在資金業(yè)務(wù)操作或數(shù)據(jù)強(qiáng)一致性要求的業(yè)務(wù)操作中,要注意使用事務(wù),保證數(shù)據(jù)更新的一致性和完整性。


SQL問(wèn)題


SQL的正確性通??梢酝ㄟ^(guò) DAO 測(cè)試來(lái)保證。 SQL問(wèn)題主要是指潛在的性能問(wèn)題和安全問(wèn)題。


要避免SQL性能問(wèn)題, 在表設(shè)計(jì)的時(shí)候就要做好索引工作。在表數(shù)據(jù)量非常大的情況下,SQL語(yǔ)句編寫要非常小心。查詢SQL需要添加必要索引,添加合適的查詢條件和查詢順序,加快查詢效率, 避免慢查; 盡量避免使用 Join, 子查詢;避免SQL注入。


SQL優(yōu)秀書籍推薦: SQL語(yǔ)言藝術(shù)


https://book.douban.com/subject/3012601/


安全問(wèn)題


安全問(wèn)題一向是互聯(lián)網(wǎng)產(chǎn)品研發(fā)中極容易被忽視、而在爆發(fā)后又極引發(fā)熱議的議題。安全和隱私是用戶的心理紅線之一。應(yīng)用、數(shù)據(jù)、資金的安全性應(yīng)當(dāng)僅次于產(chǎn)品功能的準(zhǔn)確性和使用體驗(yàn)。


安全問(wèn)題的CodeReview可參見(jiàn)檢查點(diǎn)清單:信息安全 。主要是如下措施: a. 嚴(yán)格檢查和屏蔽非法輸入; b. 對(duì)含敏感信息的請(qǐng)求加密通信; c. 業(yè)務(wù)處理后消除任何敏感私密信息的任何痕跡; d. 結(jié)果返回前在反序列化中清除敏感私密信息; e. 敏感私密信息在數(shù)據(jù)存儲(chǔ)設(shè)備中應(yīng)當(dāng)加密存儲(chǔ); f. 應(yīng)用有嚴(yán)格的角色、權(quán)限、操作、數(shù)據(jù)訪問(wèn)分級(jí)和控制; g. 切忌暴露服務(wù)器的重要的安全性信息,防止服務(wù)器被攻擊影響正常服務(wù)運(yùn)行。


設(shè)計(jì)問(wèn)題


設(shè)計(jì)問(wèn)題通常體現(xiàn)在: a. 是否有潛在的性能問(wèn)題; b. 是否有安全問(wèn)題; c. 業(yè)務(wù)變化時(shí)是否容易擴(kuò)展; d. 是否有遺漏的點(diǎn)。


較輕微的問(wèn)題


較輕微問(wèn)題是指“沒(méi)有技術(shù)難度、通過(guò)良好習(xí)慣即可避免的問(wèn)題”。


較輕微問(wèn)題一般不會(huì)造成負(fù)面影響的BUG或故障,不過(guò)建立一些好的習(xí)慣,主動(dòng)使用代碼檢測(cè)工具,消除這些較輕微錯(cuò)誤,也是一種修行。


命名不貼切


命名不貼切不會(huì)影響功能實(shí)現(xiàn),卻會(huì)誤導(dǎo)理解或增加理解難度。


方法:先查查字典,找個(gè)通俗易懂而且比較貼近的名字。可以參考 jdk 的命名、通用詞匯和行業(yè)詞匯; 作用域小的采用短命名,作用域大的采用長(zhǎng)命名。取名字是一種重要技能,—— 多少父母為此愁灰了頭!


聲明時(shí)未初始化


聲明時(shí)未初始化通常情況下都不會(huì)是問(wèn)題,因?yàn)楹竺鏁?huì)進(jìn)行賦值。不過(guò),如果賦值的過(guò)程中出現(xiàn)異常,那么可能會(huì)返回空值,從而導(dǎo)致空值異常。通常,變量聲明時(shí)賦予默認(rèn)初始值是個(gè)好習(xí)慣。


風(fēng)格與整體有不一致


工程通常求穩(wěn),一致性能更好地維護(hù)。在工程項(xiàng)目中,最好能夠遵循工程約定的風(fēng)格,在個(gè)人項(xiàng)目中可以凸顯個(gè)性風(fēng)格。Java編程一般要遵循《Java編程規(guī)范》,有追求的程序猿媛還會(huì)追求更高層次的,比如《Google Java 規(guī)范》等。


類型轉(zhuǎn)換錯(cuò)誤


編程語(yǔ)言的類型系統(tǒng)是非常重要的。如何在不同類型之間可靠地互轉(zhuǎn),尤其是在父子類型之間相互賦值,也是一個(gè)微技能。濫用類型轉(zhuǎn)換,也會(huì)導(dǎo)致BUG 。


Java 中容易出現(xiàn)的錯(cuò)誤是:a. 字符串轉(zhuǎn)數(shù)值,字符串含有非數(shù)字部分;b. JSON字符串轉(zhuǎn)對(duì)象,某個(gè)字段含有不兼容的值類型導(dǎo)致解析出錯(cuò);c. 子類型轉(zhuǎn)不兼容的父類型,滋生運(yùn)行時(shí)異常 ClassCastException;d. 相同特質(zhì)的類型不兼容。比如 Long 與 Integer 都是數(shù)值型,卻不能互轉(zhuǎn)。


類型轉(zhuǎn)換中最容易出BUG的地方是非布爾類型取反。受C語(yǔ)言的影響,很多高級(jí)語(yǔ)言支持各種數(shù)據(jù)類型轉(zhuǎn)布爾類型,比如 PHP 字符串、數(shù)組、數(shù)字等都可以轉(zhuǎn)布爾類型,相應(yīng)的就喜歡寫 if (!notBoolVar) 這種表達(dá)式, 容易隱藏看不出的BUG甚至錯(cuò)誤。


否定式風(fēng)格


變量含義、表達(dá)式語(yǔ)句傾向于使用否定式風(fēng)格,可能不知不覺(jué)耗費(fèi)大量腦細(xì)胞,因?yàn)槊看卫斫獾臅r(shí)候都要繞個(gè)彎子。 比如 isNoExpress 是否無(wú)需物流, 就有點(diǎn)繞。 為什么呢? 無(wú)需物流是針對(duì)快遞發(fā)貨的, 如果快遞發(fā)貨占發(fā)貨的90%, 無(wú)需物流只占10%,那么, isNoExpress = false 幾乎總為真。 涉及到判斷的時(shí)候,可能不得不寫 if (!isNoExpress) , 雙重否定足夠弄暈?zāi)恪?/p>


容器遍歷的結(jié)構(gòu)變更


絕大多數(shù)語(yǔ)言都承襲了 C 語(yǔ)言的 for(int i=0;i


API參數(shù)傳遞錯(cuò)誤


如果API參數(shù)有多個(gè),而且相鄰參數(shù)的類型相同,那么要特別留意是否參數(shù)順序是正確的,而不會(huì)張冠李戴。


當(dāng)然,在設(shè)計(jì)API參數(shù)的時(shí)候,就可以仔細(xì)用更精準(zhǔn)類型進(jìn)行區(qū)分,并將相同類型的參數(shù)錯(cuò)開(kāi)。比如 calc(int accountNo, int pay, int timestamp) , 就容易傳錯(cuò),比較可靠的是 calc(int accountNo, Currency pay, Timestamp now) ,這樣是不可能將參數(shù)傳遞錯(cuò)誤的。


單行調(diào)用括號(hào)過(guò)多


為了簡(jiǎn)便,常常會(huì)寫出 wapper(calc(now, String.format(“%s\n”, new BufferedFileReader(filename, “UTF-8″).readLines() ))) 的語(yǔ)句 , 嗯,你得好好瞧瞧和算算右邊的括號(hào)數(shù)量是否正確了。更糟糕的時(shí)候,結(jié)合API參數(shù)傳遞錯(cuò)誤,IDE 可能沒(méi)有報(bào)錯(cuò), 而你很可能沒(méi)有意識(shí)到自己的參數(shù)傳遞錯(cuò)誤了。 可靠的方式是, 拆出一部分變量,并將調(diào)用之間的括號(hào)用空格隔開(kāi),顯示出層次感。


String fileContent = new BufferedFileReader(filename, 'UTF-8').readLines();

wapper( calc( now,  String.format('%s\n', fileContent) ) )


修改方法簽名


對(duì)某個(gè)方法有業(yè)務(wù)改動(dòng)時(shí),程序猿媛們傾向直接修改原方法的簽名。這時(shí),要特別注意:a. 不要修改原方法的參數(shù)順序; b. 在最后面增加可選參數(shù)。 從另一個(gè)角度來(lái)看,復(fù)雜的業(yè)務(wù)方法應(yīng)當(dāng)分兩層: 最外層負(fù)責(zé)調(diào)度,方法參數(shù)具有包容性,里面包含的字段比較多 ; 內(nèi)層方法負(fù)責(zé)特定業(yè)務(wù)邏輯的實(shí)現(xiàn),方法參數(shù)少而精。


修改原方法簽名本身就是容易產(chǎn)生問(wèn)題的習(xí)慣, 篡改原方法的參數(shù)順序更是大忌。 最好的方法是新建一個(gè)方法去復(fù)用原方法, 然后調(diào)用新的方法。代碼變更始終銘記“開(kāi)閉”原則。


打印日志太多


打印過(guò)多的日志并不好。一方面遮掩真正需要的信息,導(dǎo)致排查耗費(fèi)時(shí)間, 另一方面造成服務(wù)器空間浪費(fèi)、影響性能。生產(chǎn)環(huán)境日志一般只開(kāi)放 INFO及以上級(jí)別的日志; Debug 日志只在調(diào)試或排錯(cuò)的時(shí)候使用,生產(chǎn)環(huán)境可以禁止debug日志。


多級(jí)數(shù)據(jù)結(jié)構(gòu)


使用多級(jí)數(shù)據(jù)結(jié)構(gòu)時(shí),要確定父級(jí)數(shù)據(jù)一定有值,或者進(jìn)行檢測(cè)。比如 $order['baole']['ump']['money'],必須確保 $order['baole'], $order['baole']['money'] 一定有值或做非空檢測(cè)。


作用域過(guò)大


由于C語(yǔ)言的影響,猿媛們會(huì)在開(kāi)頭就定義好一些變量或要返回的對(duì)象,在很靠后的地方才使用到。不必要的過(guò)大的作用域?qū)ψ兞亢蛯?duì)象的變化產(chǎn)生不可測(cè)的影響,并增大理解的成本。可靠的方法是,僅當(dāng)在使用時(shí)才定義,并盡快返回結(jié)果。


另一種情況是,暴露的訪問(wèn)域過(guò)大,比如 public 字段。 盡可能地縮小可訪問(wèn)的范圍,可以增大變更和重構(gòu)的空間; 減少可變性,則可以自然地獲得并發(fā)安全性,降低CodeReview的理解成本。


比如,不可變的類和字段定義成 final , 最小化包,類,接口,方法和域的可訪問(wèn)性,默認(rèn)為 private , 若需要繼承,可定義為 protected , 僅當(dāng)需要作為 API 服務(wù)暴露出去時(shí),使用 public.


分支與循環(huán)


條件與循環(huán)偶爾也會(huì)導(dǎo)致錯(cuò)誤, 不過(guò)通常錯(cuò)誤可以在發(fā)布前解決掉。


對(duì)于 if-else 嵌套條件, 需要仔細(xì)檢查是否符合業(yè)務(wù)邏輯; 如果嵌套太深,是否可以使用另一種方式“解結(jié)” ; 對(duì)于 switch 語(yǔ)句, 大多數(shù)語(yǔ)言的 case 有 fall through 問(wèn)題, 要注意加上 break ; 最好加上 default 的處理。


對(duì)于 for 循環(huán), 編寫合理的結(jié)束條件避免死循環(huán); 對(duì)于循環(huán)變量的控制, 避免出現(xiàn) -1或 +1 錯(cuò)誤, 消除越界錯(cuò)誤; for 循環(huán)也要特別注意對(duì)空值和空容器的處理,避免拋出空值異常??梢酝ㄟ^(guò)單測(cè)來(lái)確保 for 循環(huán)的準(zhǔn)確性。

看完本文有收獲?請(qǐng)轉(zhuǎn)發(fā)分享給更多人


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多