0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

15條重寫爛代碼的經(jīng)驗分享

jf_ro2CN3Fa ? 來源:撿田螺的小男孩 ? 作者:撿田螺的小男孩 ? 2022-11-11 11:10 ? 次閱讀

寫這篇文章,來記錄一下一個優(yōu)秀的后端開發(fā)程序員,應該有哪些好的開發(fā)習慣。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權限、多租戶、數(shù)據(jù)權限、工作流、三方登錄、支付、短信、商城等功能

項目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

視頻教程:https://doc.iocoder.cn/video/

1.注釋盡可能全面,寫有意義的注釋

接口方法、類、復雜的業(yè)務邏輯,都應該添加有意義的注釋

對于接口方法的注釋,應該包含詳細的入?yún)⒑徒Y果說明,有異常拋出的情況也要詳細敘述

類的注釋應該包含類的功能說明、作者和修改者。

如果是業(yè)務邏輯很復雜的代碼,真的非常有必要寫清楚注釋。

清楚的注釋,更有利于后面的維護。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權限、多租戶、數(shù)據(jù)權限、工作流、三方登錄、支付、短信、商城等功能

項目地址:https://gitee.com/zhijiantianya/yudao-cloud

視頻教程:https://doc.iocoder.cn/video/

2.項目拆分合理的目錄結構

記得讀大學那會,剛學做各種各樣的管理系統(tǒng),都是用MVC模式,也就是controller、service、mapper、entity。如果未來業(yè)務擴展,你沒有拆分業(yè)務結構的話,很可能就會發(fā)現(xiàn),一個service包下,有上百個服務。。。

正確的做法,如果服務過多,應該根據(jù)不同的業(yè)務進行劃分,比如訂單、登陸、積分等等

487a6840-6167-11ed-8abf-dac502259ad0.png

當然,你也可以根據(jù)不同的業(yè)務劃分模塊,比如建一個moudles包,然后按訂單、登陸等業(yè)務劃分,每個業(yè)務都有自己的controller、service、mapper、entity。

我們拆分的目的,就是讓項目結構更清晰,可讀性更強,更容易維護 而已。

3. 不在循環(huán)里遠程調(diào)用、或者數(shù)據(jù)庫操作,優(yōu)先考慮批量進行。

遠程操作或者數(shù)據(jù)庫操作都是比較耗網(wǎng)絡、IO資源 的,所以盡量不在循環(huán)里遠程調(diào)用、不在循環(huán)里操作數(shù)據(jù)庫,能批量一次性查回來盡量不要循環(huán)多次去查 。(但是呢,如果是操作數(shù)據(jù)庫,也不要一次性查太多數(shù)據(jù)哈,可以分批500一次醬紫)。

正例:

remoteBatchQuery(param);

反例:

for(inti=0;i

4. 封裝方法形參

如果你的方法參數(shù)過多,要封裝一個對象出來。反例如下:

publicvoidgetUserInfo(Stringname,Stringage,Stringsex,Stringmobile,StringidNo){
//dosomething...
}

如果參數(shù)很多,做新老接口兼容處理也比較麻煩。建議寫個對象出來,如下:

publicvoidgetUserInfo(UserInfoParamDTOuserInfoParamDTO){
//dosomething...
}

classUserInfoParamDTO{
privateStringname;
privateStringage;
privateStringsex;
privateStringmobile;
privateStringidNo;
}

5. 封裝通用模板

一個優(yōu)秀的后端開發(fā),應該具備封裝通用模板 的編碼能力。

我們來看一個業(yè)務需求:假設我們有這么一個業(yè)務場景:內(nèi)部系統(tǒng)不同商戶,調(diào)用我們系統(tǒng)接口,去跟外部第三方系統(tǒng)交互(http方式)。走類似這么一個流程,如下:

4889a2e2-6167-11ed-8abf-dac502259ad0.png

一個請求都會經(jīng)歷這幾個流程:

查詢商戶信息

對請求報文加簽

發(fā)送http請求出去

對返回的報文驗簽

通過HTTP發(fā)請求出去時,有的商戶可能是走代理 的,有的是走直連。假設當前有A,B商戶接入,不少伙伴可能這么實現(xiàn),偽代碼如下:

//商戶A處理句柄
CompanyAHandlerimplementsRequestHandler{
Resphander(req){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求(A商戶假設走的是代理)
httpRequestbyProxy()
//驗簽
verify();
}
}
//商戶B處理句柄
CompanyBHandlerimplementsRequestHandler{
Resphander(Rreq){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求(B商戶不走代理,直連)
httpRequestbyDirect();
//驗簽
verify();
}
}

假設新加一個C商戶接入,你需要再實現(xiàn)一套這樣的代碼。顯然,這樣代碼就重復了。這時候我們可以封裝一個通用模板 !我們就可以定義一個抽象類,包含請求流程的幾個方法,偽代碼如下:

abstractclassAbstractMerchantService{

//模板方法流程
ResphandlerTempPlate(req){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求
httpRequest();
//驗簽
verifySinature();
}
//Http是否走代理(提供給子類實現(xiàn))
abstractbooleanisRequestByProxy();
}

然后所有商戶接入,都做這個流程。如果這個通用模板是你抽取的,別的小伙伴接到開發(fā)任務,都是接入你的模板,是不是會有點自豪呀,哈哈~

封裝通用模板 ,就是抽個模板模式嘛?其實不僅僅是,而是自己對需求、代碼的思考與總結 ,一種編程思想的升華 。

6. 封裝復雜的邏輯判斷條件

我們來看下這段代碼:

publicvoidtest(UserStatususerStatus){
if(userStatus!=UserStatus.BANNED&&userStatus!=UserStatus.DELETED&&userStatus!=UserStatus.FROZEN){
//doSomeThing
return
}
}

這段代碼有什么問題呢?是的,邏輯判斷條件太復雜啦,我們可以封裝一下它 。如下:

publicvoidtest(UserStatususerStatus){
if(isUserActive(userStatus)){
//doSomeThing
}
}

privatebooleanisUserActive(UserStatususerStatus){
returnuserStatus!=UserStatus.BANNED&&userStatus!=UserStatus.DELETED&&userStatus!=UserStatus.FROZEN;
}

7. 保持優(yōu)化性能的嗅覺

優(yōu)秀的后端開發(fā),應該保持優(yōu)化性能的嗅覺。比如避免創(chuàng)建比必要的對象、異步處理、使用緩沖流,減少IO操作等等。

比如,我們設計一個APP首頁的接口,它需要查用戶信息、需要查banner信息、需要查彈窗信息等等。假設耗時如下:

48af2dbe-6167-11ed-8abf-dac502259ad0.png

查用戶信息200ms,查banner信息100ms、查彈窗信息50ms,那一共就耗時350ms了。如果還查其他信息,那耗時就更大了。如何優(yōu)化它呢?可以并行發(fā)起,耗時可以降為200ms。如下:

48c6d89c-6167-11ed-8abf-dac502259ad0.png

8. 可變參數(shù)的配置化處理

日常開發(fā)中,我們經(jīng)常會遇到一些可變參數(shù),比如用戶多少天沒登錄注銷、運營活動,不同節(jié)日紅包皮膚切換、訂單多久沒付款就刪除等等。對于這些可變的參數(shù),不用該直接寫死在代碼。優(yōu)秀的后端,要做配置化處理,你可以把這些可變參數(shù),放到數(shù)據(jù)庫一個配置表里面,也可以放到項目的配置文件或者apollo上。

比如產(chǎn)品經(jīng)理提了個紅包需求,圣誕節(jié)的時候,紅包皮膚為圣誕節(jié)相關的,春節(jié)的時候,為春節(jié)紅包皮膚等。如果在代碼寫死控制,可有類似以下代碼:

if(duringChristmas){
img=redPacketChristmasSkin;
}elseif(duringSpringFestival){
img=redSpringFestivalSkin;
}

如果到了元宵節(jié)的時候,運營小姐姐突然又有想法,紅包皮膚換成燈籠相關的,這時候,是不是要去修改代碼了,重新發(fā)布了?

從一開始接口設計時,可以實現(xiàn)一張紅包皮膚的配置表 ,將紅包皮膚做成配置化呢?更換紅包皮膚,只需修改一下表數(shù)據(jù)就好了。當然,還有一些場景適合一些配置化的參數(shù):一個分頁多少數(shù)量控制、某個搶紅包多久時間過期這些,都可以搞到參數(shù)配置化表里面。這也是擴展性思想的一種體現(xiàn)。

9. 會總結并使用工具類。

很多小伙伴,判斷一個list是否為空,會這么寫:

if(list==null||list.size()==0){
returnnull;
}

這樣寫呢,邏輯是沒什么問題的。但是更建議用工具類,比如:

if(CollectionUtils.isEmpty(list)){
returnnull;
}

日常開發(fā)中,我們既要會用工具類,更要學會自己去總結工具類。比如去文件處理工具類、日期處理工具類等等。這些都是優(yōu)秀后端開發(fā)的一些好習慣。

10. 控制方法函數(shù)復雜度

你的方法不要寫得太復雜,邏輯不要混亂,也不要太長 。一個函數(shù)不能超過80行。寫代碼不僅僅是能跑就行,而是為了以后更好的維護。

反例如下:

publicclassTest{
privateStringname;
privateVectororders=newVector();

publicvoidprintOwing(){
//printbanner
System.out.println("****************");
System.out.println("*****customerOwes*****");
System.out.println("****************");

//calculatetotalAmount
Enumerationenv=orders.elements();
doubletotalAmount=0.0;
while(env.hasMoreElements()){
Orderorder=(Order)env.nextElement();
totalAmount+=order.getAmout();
}

//printdetails
System.out.println("name:"+name);
System.out.println("amount:"+totalAmount);
......
}
}

其實可以使用Extract Method,抽取功能單一的代碼段,組成命名清晰的小函數(shù),去解決長函數(shù)問題,正例如下:

publicclassTest{
privateStringname;
privateVectororders=newVector();

publicvoidprintOwing(){

//printbanner
printBanner();
//calculatetotalAmount
doubletotalAmount=getTotalAmount();
//printdetails
printDetail(totalAmount);
}

voidprintBanner(){
System.out.println("****************");
System.out.println("*****customerOwes*****");
System.out.println("****************");
}

doublegetTotalAmount(){
Enumerationenv=orders.elements();
doubletotalAmount=0.0;
while(env.hasMoreElements()){
Orderorder=(Order)env.nextElement();
totalAmount+=order.getAmout();
}
returntotalAmount;
}

voidprintDetail(doubletotalAmount){
System.out.println("name:"+name);
System.out.println("amount:"+totalAmount);
}
}

11. 在finally塊中對資源進行釋放

應該大家都有過這樣的經(jīng)歷,windows系統(tǒng)桌面如果打開太多文件或者系統(tǒng)軟件,就會覺得電腦很卡。當然,我們linux服務器也一樣,平時操作文件,或者數(shù)據(jù)庫連接,IO資源流如果沒關閉,那么這個IO資源就會被它占著,這樣別人就沒有辦法用了,這就造成資源浪費。

我們操作完文件資源,需要在在finally塊中對資源進行釋放。

FileInputStreamfdIn=null;
try{
fdIn=newFileInputStream(newFile("/撿田螺的小男孩.txt"));
}catch(FileNotFoundExceptione){
log.error(e);
}catch(IOExceptione){
log.error(e);
}finally{
try{
if(fdIn!=null){
fdIn.close();
}
}catch(IOExceptione){
log.error(e);
}
}

12.把日志打印好

日常開發(fā)中,一定需要把日志打印好。比如:你實現(xiàn)轉賬業(yè)務,轉個幾百萬,然后轉失敗了,接著客戶投訴,然后你還沒有打印到日志,想想那種水深火熱的困境下,你卻毫無辦法。。。

一般情況,方法入?yún)?、出參需要打印日志,異常的時候,也要打印日志等等,如下:

publicvoidtransfer(TransferDTOtransferDTO){
log.info("invoketranferbegin");
//打印入?yún)?log.info("invoketranfer,paramters:{}",transferDTO);
try{
res=transferService.transfer(transferDTO);
}catch(Exceptione){
log.error("transferfail,account:{}",
transferDTO.getAccount())
log.error("transferfail,exception:{}",e);
}
log.info("invoketranferend");
}

13. 考慮異常,處理好異常

優(yōu)秀的后端開發(fā),應當考慮到異常,并做好異常處理。田螺哥給大家提了10個異常處理的建議:

盡量不要使用e.printStackTrace(),而是使用log打印。因為e.printStackTrace()語句可能會導致內(nèi)存占滿。

catch住異常時,建議打印出具體的exception,利于更好定位問題

不要用一個Exception捕捉所有可能的異常

記得使用finally關閉流資源或者直接使用try-with-resource。

捕獲異常與拋出異常必須是完全匹配,或者捕獲異常是拋異常的父類

捕獲到的異常,不能忽略它,至少打點日志吧

注意異常對你的代碼層次結構的侵染

自定義封裝異常,不要丟棄原始異常的信息Throwable cause

運行時異常RuntimeException ,不應該通過catch的方式來處理,而是先預檢查,比如:NullPointerException處理

注意異常匹配的順序,優(yōu)先捕獲具體的異常

14. 考慮系統(tǒng)、接口的兼容性

優(yōu)秀的后端開發(fā),會考慮系統(tǒng)、接口的兼容性。

如果修改了對外舊接口,但是卻不做兼容。這個問題可能比較嚴重,甚至會直接導致系統(tǒng)發(fā)版失敗的。新手程序員很容易犯這個錯誤哦~

因此,如果你的需求是在原來接口上修改,尤其這個接口是對外提供服務的話,一定要考慮接口兼容。舉個例子吧,比如dubbo接口,原本是只接收A,B參數(shù),現(xiàn)在你加了一個參數(shù)C,就可以考慮這樣處理:

//老接口
voidoldService(A,B){
//兼容新接口,傳個null代替C
newService(A,B,null);
}

//新接口,暫時不能刪掉老接口,需要做兼容。
voidnewService(A,B,C){
...
}

15. 采取措施避免運行時錯誤

優(yōu)秀的后端開發(fā),應該在編寫代碼階段,就采取措施,避免運行時錯誤 ,如數(shù)組邊界溢出,被零整除,空指針等運行時錯誤。類似代碼比較常見:

Stringname=list.get(1).getName();//list可能越界,因為不一定有2個元素哈

所以,應該采取措施,預防一下數(shù)組邊界溢出,正例如下:

if(CollectionsUtil.isNotEmpty(list)&&list.size()>1){
Stringname=list.get(1).getName();
}





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux系統(tǒng)

    關注

    4

    文章

    587

    瀏覽量

    27181
  • MVC
    MVC
    +關注

    關注

    0

    文章

    73

    瀏覽量

    13823
  • HTTP協(xié)議

    關注

    0

    文章

    56

    瀏覽量

    9679

原文標題:用1個月重構了同事寫的爛代碼,我總結出了15條重寫爛代碼的經(jīng)驗!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    100FPGA經(jīng)驗

    100FPGA經(jīng)驗 非常值得收藏 100FPGA經(jīng)驗 非常值得收藏
    發(fā)表于 11-11 17:01 ?23次下載

    總結模擬電路設計經(jīng)驗12

    模擬電子的相關知識學習教材資料——總結模擬電路設計經(jīng)驗12
    發(fā)表于 09-20 16:10 ?6次下載

    模擬電路設計經(jīng)驗12

    模擬電子的相關知識學習教材資料——模擬電路設計經(jīng)驗12
    發(fā)表于 09-27 15:19 ?0次下載

    代碼你能忍嗎?優(yōu)秀的代碼VS糟糕的代碼

    糟糕的代碼原來那么不堪一擊。
    的頭像 發(fā)表于 03-30 10:09 ?4315次閱讀
    <b class='flag-5'>爛</b><b class='flag-5'>代碼</b>你能忍嗎?優(yōu)秀的<b class='flag-5'>代碼</b>VS糟糕的<b class='flag-5'>代碼</b>

    垃圾代碼應該怎么寫

    在 GitHub 上有一個新項目,它描述了「最佳垃圾代碼」的十九關鍵準則。從變量命名到注釋編寫。這些準則將指導你寫出最亮眼的代碼。 為了保持與原 GitHub 項目一致的風格,下文
    的頭像 發(fā)表于 01-18 11:08 ?1914次閱讀

    高速PCB布線經(jīng)驗分享

    讓你布線少走彎道的15高速PCB布線經(jīng)驗分享
    的頭像 發(fā)表于 02-12 10:44 ?5748次閱讀

    15高速PCB布線經(jīng)驗分享

    讓你布線少走彎道的15高速PCB布線經(jīng)驗分享
    發(fā)表于 03-04 06:24 ?31次下載
    <b class='flag-5'>15</b><b class='flag-5'>條</b>高速PCB布線<b class='flag-5'>經(jīng)驗</b>分享

    STM32 40知識,開發(fā)經(jīng)驗必備資料下載

    電子發(fā)燒友網(wǎng)為你提供STM32 40知識,開發(fā)經(jīng)驗必備資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
    發(fā)表于 04-04 08:48 ?24次下載
    STM32 40<b class='flag-5'>條</b>知識,開發(fā)<b class='flag-5'>經(jīng)驗</b>必備資料下載

    PCB電源設計13經(jīng)驗資料下載

    電子發(fā)燒友網(wǎng)為你提供PCB電源設計13經(jīng)驗資料下載的電子資料下載,更有其他相關的電路圖、源代碼、課件教程、中文資料、英文資料、參考設計、用戶指南、解決方案等資料,希望可以幫助到廣大的電子工程師們。
    發(fā)表于 04-21 08:54 ?15次下載
    PCB電源設計13<b class='flag-5'>條</b><b class='flag-5'>經(jīng)驗</b>資料下載

    PID算法原理_調(diào)試經(jīng)驗以及代碼總結

    PID算法原理_調(diào)試經(jīng)驗以及代碼總結分享。
    發(fā)表于 05-25 15:59 ?16次下載

    PID算法原理、調(diào)試經(jīng)驗以及代碼資料

    PID算法原理、調(diào)試經(jīng)驗以及代碼資料
    發(fā)表于 11-21 10:25 ?15次下載

    Rust重寫的LSP:KCL IDE 插件的功能介紹與設計解析

    在這次更新中,我們發(fā)布了全新的 KCL VS Code 插件,并且用 Rust 重寫了 LSP 的 Server 端。我們提供了 IDE 中常用的代碼輔助功能,如高亮、跳轉、補全、Outline、懸停、錯誤提示等。
    的頭像 發(fā)表于 05-11 09:39 ?845次閱讀
    Rust<b class='flag-5'>重寫</b>的LSP:KCL IDE 插件的功能介紹與設計解析

    Windows 11初嘗Rust,36000行內(nèi)核代碼重寫!

    更早些時候,微軟用 Rust 重寫了 DirectWrite Core 庫的概念驗證,它是 Windows 的 DWrite 引擎的 Windows App SDK 實現(xiàn),用于文本分析、布局和渲染
    的頭像 發(fā)表于 05-19 16:39 ?898次閱讀
    Windows 11初嘗Rust,36000行內(nèi)核<b class='flag-5'>代碼</b>已<b class='flag-5'>重寫</b>!

    大彩串口屏控件教程15 - 圓形進度控件應用

    大彩串口屏控件教程15-圓形進度控件應用
    發(fā)表于 04-29 12:57 ?3次下載

    【AWTK使用經(jīng)驗】如何設計立體電池進度

    AWTK是基于C語言開發(fā)的跨平臺GUI框架。《AWTK使用經(jīng)驗》系列文章將介紹開發(fā)AWTK過程中一些常見問題與解決方案,例如:如何加載外部資源?如何設計自定義進度?這些都會在系列文章進行解答
    的頭像 發(fā)表于 04-18 08:25 ?328次閱讀
    【AWTK使用<b class='flag-5'>經(jīng)驗</b>】如何設計立體電池進度<b class='flag-5'>條</b>?