中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

CodeReview常見代碼問題

2018-07-20    來源:importnew

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

路線圖

常見代碼問題

常見的潛在代碼問題是當(dāng)前直接會導(dǎo)致BUG、故障或者產(chǎn)品功能不能正常工作的類別。

空值

空值恐怕是最容易出現(xiàn)的地方之一。 常見錯誤有: a. 值為NULL導(dǎo)致空指針異常; b. 參數(shù)字符串含有前導(dǎo)或后綴空格沒有Trim導(dǎo)致查詢?yōu)榭铡?導(dǎo)致以上結(jié)果的原因主要有: 無此記錄、有此記錄但由于SQL訪問異常而沒查到、網(wǎng)絡(luò)調(diào)用失敗、記錄中有臟數(shù)據(jù)、參數(shù)沒傳。

原則上,對于任何異常, 希望能夠打印出具體的錯誤信息,根據(jù)錯誤信息很快明白是什么原因, 而不是一個 null ,還要在代碼里去推敲為什么為空。這樣我們必須識別出程序中可能的null, 并及時檢測、捕獲和拋出異常。

對于空值,最好的防護(hù)是“防御式編程”。當(dāng)獲取到對象之后, 使用之前總是判斷是否為空,并適當(dāng)拋出異常、打錯誤日志或做其它處理。 有的人嫌檢測為空的 if 語句充斥在代碼里會破壞代碼的可維護(hù)性, 對此我的建議是:

  • 空值檢測一定要有, 有勝于無。
  • 在空值檢測總是存在的前提下, 可以優(yōu)化空值檢測的方法和存在形式。 比如集中于一個類 NullChecker 中管理,并與系統(tǒng)的整體錯誤處理設(shè)計保持一致。集中管理和處理一致性原則可以作為系統(tǒng)設(shè)計的一個準(zhǔn)則。 這樣主流程中只要增加一行調(diào)用即可, 既可以天網(wǎng)恢恢疏而不漏地檢測對象為空, 也不會讓代碼顯得難看。
class NullChecker {
       public static void checkNull(Object obj, Error error) {
               if (obj == null)  { throw new BizException(error); }
       }
}
  • 在參數(shù)入口處統(tǒng)一做 trim。 如果在業(yè)務(wù)邏輯里做 trim , 就會導(dǎo)致有的業(yè)務(wù)邏輯做了 trim , 有的沒做, 體現(xiàn)在產(chǎn)品上就會有令用戶困惑的事情發(fā)生。 比如搜索和導(dǎo)出業(yè)務(wù), 搜索能搜索出來, 導(dǎo)出卻沒有。

未捕獲潛在的異常

第二個容易出錯的地方是未捕獲潛在的異常。調(diào)用API接口、庫函數(shù)或系統(tǒng)服務(wù)等,只顧著享受便利卻不做防護(hù),常導(dǎo)致因為局部失敗而影響整體的功能。最好的防護(hù)依然是“防御式編程”。 要么在當(dāng)前方法捕獲異常并返回合適的空值或空對象,要么拋給高層處理。

切不可默默”吞掉錯誤和異常”。 如果這樣做了, 出問題了等著加班和耗費大量腦細(xì)胞吧!
在CodeReview的時候一定要仔細(xì)詢問:這里是否可能會拋出異常?如果拋異常會怎么處理?是否會影響整體服務(wù)和返回結(jié)果?

低性能

低性能會導(dǎo)致產(chǎn)品功能不好用、不可用,甚至導(dǎo)致產(chǎn)品失敗。

常見情況有:a. 循環(huán)地逐個調(diào)用單個接口獲取數(shù)據(jù)或訪問數(shù)據(jù)庫; b. 重復(fù)創(chuàng)建幾乎完全相同的(開銷大的)對象;c. 數(shù)據(jù)庫訪問、網(wǎng)絡(luò)調(diào)用等服務(wù)未處理超時的情況; d. 多重循環(huán)對于大數(shù)據(jù)量處理的算法性能低;e. 大量字符串拼接時使用了String而非StringBuilder.

對于 a,最好提供批量接口或批量并發(fā)獲取數(shù)據(jù); 對于 b, 將可復(fù)用對象抽離出循環(huán),一次創(chuàng)建多次使用; 對于 c,設(shè)置合理的超時時間并捕獲超時異常處理; 對于 d,使用預(yù)排序或預(yù)處理, 構(gòu)造合適的數(shù)據(jù)結(jié)構(gòu), 使得算法平均性能在 O(n) 或 O(nlogn) ; 對于 e, 記。 少量字符串拼接使用String, 大量字符串拼接使用 StringBuilder, 通常不會使用到 StringBuffer.

影響范圍過大

對多個模塊依賴的公共函數(shù)的修改,容易造成影響范圍超過當(dāng)前業(yè)務(wù)改動,無意識地破壞依賴于該公共函數(shù)的其他業(yè)務(wù)。要特別慎重。可靠的方式是:先查看該公共函數(shù)的調(diào)用, 如果只有自己的業(yè)務(wù)用,可適當(dāng)大膽一些; 如果有多個地方依賴,抽離一個新的函數(shù),抽離原函數(shù)里的可復(fù)用部分,然后基于可復(fù)用部分構(gòu)建新的函數(shù)。修改原則遵循“開閉”原則,才能盡可能使改動影響降低到最小化。

基類及實例字段和方法也屬于公共函數(shù)的范疇。 盡量不要修改基類的東西。

單測問題

單測是保證工程質(zhì)量的第一道重要防線。單測問題一般包括: a. 單測未全部通過; b. 重要業(yè)務(wù)邏輯缺乏單測; c. 缺乏異常單測; d. 代碼變更或BUG修復(fù)缺乏單測。

單測全部通過應(yīng)當(dāng)是提交代碼到代碼庫以及代碼Review的前提條件。代碼提交者應(yīng)當(dāng)保證單測全部通過。沒有捷徑可走。僅當(dāng)單測全部通過才提交到代碼庫, 可以通過工具自動化實現(xiàn)。 對于 maven 管理的工程, 只需一個命令: mvn test && git push origin branch_name 。 單測應(yīng)當(dāng)更注重質(zhì),而非單純追求覆蓋率。

缺乏單測的重要業(yè)務(wù)邏輯就像裸露在空氣中的電線一樣,雖然能跑起來,卻是很容易“觸電”的。 方法: 增加覆蓋比較全面的單測。

缺乏異常單測也是代碼提交者常忽略的問題。 異常也是一種實際的業(yè)務(wù)場景,反映系統(tǒng)的健壯性和友好性。異常應(yīng)該有相應(yīng)的單元測試覆蓋。創(chuàng)建條件使之拋出異常,并判斷異常是否是指定異常;若沒有拋出異;蛘卟皇侵付ó惓#瑒t應(yīng)該 AssertFailed 而不是通過。

對于代碼變更和BUG修復(fù),如果當(dāng)時由于時間緊而沒有寫,后續(xù)應(yīng)當(dāng)補上。對于每個代碼變更和BUG,都可以抽離出相應(yīng)的代碼部分, 并有相應(yīng)單測覆蓋,并注明原因。

與原有業(yè)務(wù)邏輯不兼容

改動針對當(dāng)前需求是合理的,卻與原有業(yè)務(wù)邏輯不兼容,也是常見的問題。比如增加一個搜索條件, 卻不能與原有條件聯(lián)合查詢。

與原有業(yè)務(wù)不兼容, 一般出現(xiàn)在:

  1. 一對一與一對多的變化。 比如原來的關(guān)系是一個訂單對應(yīng)一個物流信息, 后來變化為一個訂單可能對應(yīng)多個物流信息; 原來的邏輯是一個訂單顯示多個物流信息可以更改,后來要求一個訂單只展示最近一次的物流信息可以修改。
  2. 多個業(yè)務(wù)組合。 業(yè)務(wù) A 與業(yè)務(wù) B 原來是分開發(fā)展的, 后來開展一種活動,將業(yè)務(wù)A與業(yè)務(wù)B進(jìn)行一種組合營銷。 此時,多半會出現(xiàn)很多 if-else 語句。

業(yè)務(wù)邏輯的兼容問題一般體現(xiàn)在系統(tǒng)的復(fù)用性和可擴展機制上。良好的系統(tǒng)可復(fù)用性和可擴展性可以更容易地做到業(yè)務(wù)邏輯兼容。 主要有如下幾種級別:

  1. 自動兼容。 增加一種類型, 只是 biz_type 的值多了一種, 系統(tǒng)自動將已有功能適配給新的 biz_type;
  2. 一點改動。增加一個分支語句, 對 biz_type 的某個特性進(jìn)行擴展;
  3. 一些改動。 需要見縫插針地增加一個單獨的分支判斷和邏輯處理模塊, 對整體可擴展性沒有影響, 但會造成局部的復(fù)雜化;
  4. 一部分功能改動。 只需要對其中一個功能模塊做個擴展;
  5. 多處改動。 需要對多個功能模塊做相應(yīng)的改造,不過更多是新增而不是修改;
  6. 難以改動。 需要深入到功能模塊內(nèi)部做艱難的修改, 并要保證原有功能不受影響。

如何應(yīng)對呢?

  1. 針對關(guān)聯(lián)關(guān)系, 在項目之初, 可以詢問清楚: 將來在產(chǎn)品上是否有可擴展的變化? 及早預(yù)留空間, 或者確定產(chǎn)品上的對策; 在代碼實現(xiàn)上, 兼顧考慮一對一到一對多,或一對多到一對一的關(guān)聯(lián)變化。比如使用列表來表達(dá)單個信息, 使用索引從列表中獲取單個信息。
  2. 針對業(yè)務(wù)組合, 明確各業(yè)務(wù)的核心部分, 抽離出業(yè)務(wù)的可復(fù)用的部分,形成 API ; 考慮組合模式和裝飾器模式來進(jìn)行擴展。

核心不變, 外圍定制化。

缺乏必要日志

對于重要而關(guān)鍵的實例狀態(tài)、代碼路徑及API調(diào)用,應(yīng)當(dāng)添加適當(dāng)?shù)腎NFO日志;對于異常,應(yīng)當(dāng)捕獲并添加Error日志。缺乏日志并不會影響業(yè)務(wù)功能,但出現(xiàn)問題排查時,就會非常不方便,甚至錯失極寶貴的機會(不易重現(xiàn)的情況尤其如此)。此外,缺乏日志也會導(dǎo)致可控性差,難以做數(shù)據(jù)統(tǒng)計和分析。

錯誤碼不符合規(guī)范

錯誤碼本身不算是代碼問題,不過基于整個組織和工程的可維護(hù)性來說,可以將錯誤碼不符合規(guī)范作為一種錯誤加以避免。方法: 對錯誤碼進(jìn)行可控的管理和遵循規(guī)范使用?梢允褂霉参臋n維護(hù), 也可以開發(fā)錯誤碼管理系統(tǒng)來避免相同的錯誤碼。

參數(shù)檢測缺乏或不足

參數(shù)檢測是對業(yè)務(wù)處理的第一層重要過濾。如果參數(shù)檢測不足夠,就會導(dǎo)致臟數(shù)據(jù)進(jìn)入服務(wù)處理,輕則導(dǎo)致異常,重則插入臟數(shù)據(jù)到數(shù)據(jù)庫,對后續(xù)維護(hù)都會造成很多維護(hù)成本。方法: 采用“契約式編程”,規(guī)定前置條件,并使用單測進(jìn)行覆蓋。

對于復(fù)雜的業(yè)務(wù)應(yīng)用, 優(yōu)雅的參數(shù)檢測處理尤為重要。 根據(jù) “集中管理和處理一致性原則”, 可以建立一個 paramchecker 包, 設(shè)計一個可復(fù)用的微框架來對應(yīng)用中所有的參數(shù)進(jìn)行統(tǒng)一集中化檢測。參數(shù)檢測主要包括: (1) 參數(shù)的值類型, 可以根據(jù)不同值類型做基礎(chǔ)的檢測; (2) 參數(shù)的業(yè)務(wù)類型, 有基礎(chǔ)非業(yè)務(wù)參數(shù), 基礎(chǔ)業(yè)務(wù)參數(shù)和具體業(yè)務(wù)參數(shù)。 不同的參數(shù)業(yè)務(wù)類型有不同的處理。 將參數(shù)值類型與參數(shù)業(yè)務(wù)類型結(jié)合起來, 結(jié)合一致性的異常捕獲處理, 就可以實現(xiàn)一個可復(fù)用的參數(shù)檢測框架。參數(shù)檢測既可以采用普通的分支語句,也可以采用注解方式。采用注解方式更可讀,不過單測編寫更具技巧。

引用錯誤

對于動態(tài)語言, 由于缺乏強大的靜態(tài)代碼檢測,修改了類引用的地方尤其要注意,很可能導(dǎo)致依賴的其他業(yè)務(wù)出錯; 尤其是修改重名引用時。有線上故障教訓(xùn)。PHP工程中含有兩個 Format 類, 一個基礎(chǔ)的一個業(yè)務(wù)相關(guān)的, 被改動的類文件里開始沒有指明引用,默認(rèn)采用了基礎(chǔ) Format 類的實現(xiàn), 然后提交者在改動文件頭增加了對業(yè)務(wù) Format 的引用, 導(dǎo)致依賴于基礎(chǔ)Format類的其他業(yè)務(wù)不能正常工作。避免引用錯誤的方法: 當(dāng)要在文件里增加新的類引用時, 先在文件里搜索是否有重名類的引用。如果有, 就要格外小心了。

細(xì)節(jié)錯誤

比如數(shù)組越界、JSON解析出錯、函數(shù)參數(shù)傳遞出錯、API 版本不對、使用網(wǎng)上拷貝的未經(jīng)測試的代碼、不成熟的算法、傳值與傳引用、相等性比較等。

對于數(shù)組越界錯誤, 通常要對空數(shù)組、針對數(shù)組大小的邊界值+1和-1寫單測來避免; 使用網(wǎng)上拷貝的代碼,誠然可節(jié)省時間,也一定要加工一下并用單測覆蓋; 傳值和傳引用可通過單測來避免錯誤; 對象的相等性比較切忌使用等號=。

多重條件

類似 if ((!A || !B) && C || (D && E)) 的多重條件要仔細(xì)推敲。方法: 最好拆分成多個有含義變量。 isNotDelay = !A || !B ; isNormal = C ; isAllow = D && E ; cond = isNotDelay && isNormal || isAllow 。

文不符實

文不符實是一種可能導(dǎo)致線上故障的錯誤。比如一個 getXXX 的函數(shù),結(jié)果里面還做了 add, update 的操作。對問題排查、產(chǎn)品運維等都有非常大的殺傷力。因此命名一定要用實質(zhì)內(nèi)容相符,除非是故意搞破壞。

跨語言或跨系統(tǒng)交互

稍具規(guī)模的互聯(lián)網(wǎng)創(chuàng)業(yè)公司通常會采用多語言開發(fā),比如PHP作為前端,Java作為后臺服務(wù)。當(dāng)動態(tài)類型語言與靜態(tài)類型語言交互時,會有一些問題產(chǎn)生。比如PHP的對象通常是一個Map, 如果是空對象就會寫成 [], 然而 [] 會被 Java 解析成列表。這樣, 如果數(shù)據(jù)庫的值是通過 PHP 寫入,那么這個值既有可能是JSON對象字符串,也可能是空數(shù)組字符串, Java 來解析就有點尷尬了。 同樣,當(dāng) Java 調(diào)用 PHP 接口時, 不規(guī)范的PHP接口既可能返回列表,也可能返回 true or false , Java 解析返回結(jié)果也會比較尷尬。 因此, 在跨語言交互的邊界處,要特別注意這些類型轉(zhuǎn)換的差異。

跨系統(tǒng)交互則主要是接口設(shè)計與約定的問題。同一個項目里不同業(yè)務(wù)團隊之間的業(yè)務(wù)接口設(shè)計與約定, 不同企業(yè)里開放接口的設(shè)計與約定, 要在最初深思熟慮,一旦開放,在后期很少有接口設(shè)計改動的空間。開放接口設(shè)計要符合小而美、正交的特性, 命名要貼切一致, 參數(shù)取值要指明約束,枚舉參數(shù)要給出列表, 結(jié)果返回要規(guī)范一致,可以采用通用的 {“code”:200, “msg”: “success”, “data”: xxx} ?缦到y(tǒng)交互也要統(tǒng)一對術(shù)語和接口的理解的一致。

可維護(hù)性問題

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

硬編碼

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

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

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

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

重復(fù)代碼

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

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

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

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

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

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

直接在原方法里加邏輯

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

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

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

代碼層次不合理

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

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

不用多余的代碼

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

使用全局變量

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

缺乏必要的注釋

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

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

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

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

并發(fā)

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

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

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

對于 c, 并發(fā)訪問數(shù)據(jù)庫時,要特別注意時序和狀態(tài)同步。如果時序控制不對,會導(dǎo)致狀態(tài)同步和更新出錯。

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

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

事務(wù)

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

SQL問題

SQL的正確性通?梢酝ㄟ^ DAO 測試來保證。 SQL問題主要是指潛在的性能問題和安全問題。

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

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

安全問題

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

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

設(shè)計問題

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

較輕微的問題

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

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

命名不貼切

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

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

聲明時未初始化

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

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

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

類型轉(zhuǎn)換錯誤

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

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

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

否定式風(fēng)格

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

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

絕大多數(shù)語言都承襲了 C 語言的 for(int i=0;i<N;i++) 循環(huán)形式。不過,現(xiàn)代編程語言通常都提供了迭代器遍歷、或 foreach 遍歷。 foreach 遍歷通;诘鞅闅v實現(xiàn)。 只要對容器結(jié)構(gòu)不做變更,推薦使用 foreach ; 若要遍歷的同時做修改或更新,推薦迭代器模式。 遍歷容器的時候同時做刪除元素操作,要特別留意,很可能導(dǎo)致越界錯誤。更可靠的方式時,直接生成新的容器,如果不涉及空間效率的話。

API參數(shù)傳遞錯誤

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

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

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

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

String fileContent = new BufferedFileReader(filename, "UTF-8").readLines();
wapper( calc( now,  String.format("%s\n", fileContent) ) )

修改方法簽名

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

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

打印日志太多

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

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

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

作用域過大

由于C語言的影響,猿媛們會在開頭就定義好一些變量或要返回的對象,在很靠后的地方才使用到。不必要的過大的作用域?qū)ψ兞亢蛯ο蟮淖兓a(chǎn)生不可測的影響,并增大理解的成本?煽康姆椒ㄊ,僅當(dāng)在使用時才定義,并盡快返回結(jié)果。

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

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

分支與循環(huán)

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

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

對于 for 循環(huán), 編寫合理的結(jié)束條件避免死循環(huán); 對于循環(huán)變量的控制, 避免出現(xiàn) -1或 +1 錯誤, 消除越界錯誤; for 循環(huán)也要特別注意對空值和空容器的處理,避免拋出空值異常。可以通過單測來確保 for 循環(huán)的準(zhǔn)確性。

標(biāo)簽: Google 安全 大數(shù)據(jù) 代碼 服務(wù)器 服務(wù)器空間 互聯(lián)網(wǎng) 互聯(lián)網(wǎng)產(chǎn)品 權(quán)限 數(shù)據(jù)庫 搜索 通信 網(wǎng)絡(luò) 信息安全

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:細(xì)說 Java 中的字符和字符串( 二 )

下一篇:ActionView 1.5.0 發(fā)布,更好用的問題需求跟蹤工具