What I learned from doing 1000 code reviews
文章作者從以往代碼審核的經驗角度提出三個建議
建議1诚隙、Throw an exception when things go wrong(當程序出現(xiàn)錯誤時宴霸,拋出一個異常)
例如代碼:
List<String> getSearchResults(...) {
try {
List<String> results = // make REST call to search service
return results;
} catch (RemoteInvocationException e) {
return Collections.emptyList();
}
}
核心內容:該建議主要討論在某些查詢功能中况增,當前程序發(fā)請求到后臺獲取數據時击碗,后臺跑出異常。
程序是返回一個空對象(null瓶竭、空集合等等)视乐,還是拋出異常?
場景1:查詢交易蔬墩,如果上游出現(xiàn)報錯译打,下游捕獲只返回空對象。
問題:前端只收到查詢成功拇颅,且收到空查詢結果奏司。導致監(jiān)控程序不能及時發(fā)現(xiàn)異常并及時修復問題。
場景2:從字符串解析出URL錯誤時樟插,直接返回null值韵洋。
問題:URL錯誤問題應該在上游程序修復,不能讓來下流程序進行處理黄锤。
最終結論:Empty objects are not the right tool for this job. If something is exceptional you should throw an exception(空對象不是一個正確方式針對這種情況搪缨。除非有例外清楚,不然就應該拋出一個異常)
個人觀點:個人從原則上比較贊同作者觀點鸵熟。有時候返回空對象副编,下游無法判斷具體沒有數據,還會異常流强。而且拋出異常我們盡量區(qū)分不同異常痹届。針對某一下流程序來說,捕獲到異常不一定意味程序就要中止煮盼,存在需要通過異常類型短纵,進行不同邏輯處理。
建議2僵控、Use the most specific type possible(盡可能使用最準確的類型)
作者舉例子:
void doOperation(String opType, Data data);
// where opType is "insert", "append", or "delete", this should have clearly been an enum
String fetchWebsite(String url);
// where url is "https://google.com", this should have been an URN
String parseId(Input input);
// the return type is String but ids are actually Longs like "6345789"
核心內容:使用最準確的類型香到,這樣能夠避免一些BUG,且也是基于JAVA作為強類型語言的設計报破。
具體做法:
外部數據不為準確數據類型:
1悠就、在URLS的參數
2、JSON
3充易、數據庫不支持enums
4梗脾、寫得不好的庫
處理方式:
// Step 1: Take a query param representing a company name / member id pair and parse it
// example: context=Pair(linkedin,456)
Pair<String, Long> companyMember = parseQueryParam("context");
// this should throw an exception if malformed
// Step 2: Do all the stuff in your application
// MOST if not all of your code should live in this area
// Step 3: Convert the parameter back into a String at the very end if necessary
String redirectLink = serializeQueryParam("context");
步驟:
1、獲取查詢參數 公司名稱/成員ID 并解析它盹靴。如果數據錯誤則必須拋出異常炸茧。
2瑞妇、如果不是所有代碼都應該存在于此區(qū)域,請盡可能地完成應用程序中的所有操作
3梭冠、如果有必要辕狰,在最后時候把參數轉化為String
優(yōu)化:
1、錯誤數據會被發(fā)現(xiàn)
2控漠、如果出現(xiàn)問題蔓倍,程序較早失敗中止。
3盐捷、數據被驗證過一次偶翅,之后不需要在整個程序都保持捕獲解析錯誤。
4碉渡、更加準確訪問描述聚谁,對應方法的文檔內容也減少
個人觀點:這個在原來開發(fā)習慣中一直沒有注意這個問題。尤其屬于數據字典類型確實使用enums可以避免很多意外情況滞诺,就不用通過if或switch來單獨判斷垦巴,減少很多BUG。
建議3铭段、Use Optionals instead of nulls(使用空容器代表null值)
核心內容:用JAVA 8 提供的Optional來替換null,其中解決最多就是 Null Pointer Exception的問題
使用范圍建議:
1秦爆、當使用Optional時序愚,就不用考慮對象是否存在或合理默認值。而且隨時調用get()方法等限。
2爸吮、如果沒有合理默認值,使用map()和 .flatMap()方法望门,等后面再對對象賦值形娇。
3、如果有外部庫返回null表示空結果筹误,則利用Optional.ofNullable來分裝這個值桐早。null在程序里面造成影響會慢慢擴大,所以在源頭就解決這個問題厨剪。
4哄酝、使用Optional作為方法返回值,就不需要判斷什么時候返回值是不存在祷膳。
個人觀點:目前還是很少用JAVA 8的Optional對象陶衅,需要進一步學習。但是非常贊同作者這個建議直晨,null會導致很多NPE問題
建議4搀军、“Unlift” methods whenever possible(使用空容器代表null值)
需要避免方法如下
// AVOID:
CompletableFuture<T> method(CompletableFuture<S> param);
// PREFER:
T method(S param);
// AVOID:
List<T> method(List<S> param);
// PREFER:
T method(S param);
// AVOID:
T method(A param1, B param2, Optional<C> param3);
// PREFER:
T method(A param1, B param2, C param3);
T method(A param1, B param2);
// This method is clearly doing two things, it should be two methods
// The same is true for boolean parameters
這些需要避免方法有什么相同之處呢膨俐?
1、入參都是Optional, List, Task罩句。
2焚刺、返回參數也是同樣的集合(例如入參為Optional,返回參數為Optional)
理由:
方法1:Promise<A> method(Promise<B> param)
方法2:A method(B param)
理由如下:
1的止、例如當你只有B對象時候檩坚,只能調用方法2。
2诅福、方法2重寫使得更加靈活匾委、更加容易使用
個人觀點:目前還有無法參透這條建議。如果按照作者的建議氓润,對方法進行升級赂乐。但是如果過多使用泛型感覺方法的準確度就不高。目前還在理解中咖气。