<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>

          優(yōu)秀的后端應(yīng)該有哪些開發(fā)習(xí)慣?

          共 6958字,需瀏覽 14分鐘

           ·

          2022-06-07 16:02

          前言

          前后也待過幾家公司,碰到各種各樣的同事,見識過各種各樣的代碼,優(yōu)秀的、垃圾的、不堪入目的、看了想跑路的等等,所以這篇文章記錄一下一個優(yōu)秀的后端 Java 開發(fā)應(yīng)該有哪些好的開發(fā)習(xí)慣。

          拆分合理的目錄結(jié)構(gòu)

          受傳統(tǒng)的 MVC 模式影響,傳統(tǒng)做法大多是幾個固定的文件夾?controller、service、mapper、entity,然后無限制添加,到最后你就會發(fā)現(xiàn)一個?service?文件夾下面有幾十上百個 Service 類,根本沒法分清業(yè)務(wù)模塊。正確的做法是在寫?service?上層新建一個?modules?文件夾,在?moudles?文件夾下根據(jù)不同業(yè)務(wù)建立不同的包,在這些包下面寫具體的?service、controller、entity、enums?包或者繼續(xù)拆分。



          等以后開發(fā)版本迭代,如果某個包可以繼續(xù)拆領(lǐng)域就繼續(xù)往下拆,可以很清楚的一覽項目業(yè)務(wù)模塊。后續(xù)拆微服務(wù)也簡單。

          封裝方法形參

          當你的方法形參過多時請封裝一個對象出來...... 下面是一個反面教材,誰特么教你這樣寫代碼的!

          public void updateCustomerDeviceAndInstallInfo(long customerId, String channelKey,                   String androidId, String imei, String gaId,                   String gcmPushToken, String instanceId) {}

          寫個對象出來

          public class CustomerDeviceRequest {    private Long customerId;    //省略屬性......}

          為什么要這么寫?比如你這方法是用來查詢的,萬一以后加個查詢條件是不是要修改方法?每次加每次都要改方法參數(shù)列表。封裝個對象,以后無論加多少查詢條件都只需要在對象里面加字段就行。而且關(guān)鍵是看起來代碼也很舒服啊!

          封裝業(yè)務(wù)邏輯

          如果你看過“屎山”你就會有深刻的感觸,這特么一個方法能寫幾千行代碼,還無任何規(guī)則可言......往往負責(zé)的人會說,這個業(yè)務(wù)太復(fù)雜,沒有辦法改善,實際上這都是懶的借口。不管業(yè)務(wù)再復(fù)雜,我們都能夠用合理的設(shè)計、封裝去提升代碼可讀性。下面貼兩段高級開發(fā)(假裝自己是高級開發(fā))寫的代碼

          @Transactionalpublic ChildOrder submit(Long orderId, OrderSubmitRequest.Shop shop) {    ChildOrder childOrder = this.generateOrder(shop);    childOrder.setOrderId(orderId);    //訂單來源 APP/微信小程序    childOrder.setSource(userService.getOrderSource());    // 校驗優(yōu)惠券    orderAdjustmentService.validate(shop.getOrderAdjustments());    // 訂單商品    orderProductService.add(childOrder, shop);    // 訂單附件    orderAnnexService.add(childOrder.getId(), shop.getOrderAnnexes());    // 處理訂單地址信息    processAddress(childOrder, shop);    // 最后插入訂單    childOrderMapper.insert(childOrder);    this.updateSkuInventory(shop, childOrder);    // 發(fā)送訂單創(chuàng)建事件    applicationEventPublisher.publishEvent(new ChildOrderCreatedEvent(this, shop, childOrder));    return childOrder;}
          @Transactionalpublic void clearBills(Long customerId) {    // 獲取清算需要的賬單、deposit等信息    ClearContext context = getClearContext(customerId);    // 校驗金額合法    checkAmount(context);    // 判斷是否可用優(yōu)惠券,返回可抵扣金額    CouponDeductibleResponse deductibleResponse = couponDeducted(context);    // 清算所有賬單    DepositClearResponse response = clearBills(context);    // 更新 l_pay_deposit    lPayDepositService.clear(context.getDeposit(), response);    // 發(fā)送還款對賬消息    repaymentService.sendVerifyBillMessage(customerId, context.getDeposit(), EventName.DEPOSIT_SUCCEED_FLOW_REMINDER);    // 更新賬戶余額    accountService.clear(context, response);    // 處理清算的優(yōu)惠券,被用掉或者解綁    couponService.clear(deductibleResponse);    // 保存券抵扣記錄    clearCouponDeductService.add(context, deductibleResponse);}

          這段兩代碼里面其實業(yè)務(wù)很復(fù)雜,內(nèi)部估計保守干了五萬件事情,但是不同水平的人寫出來就完全不同,不得不贊一下這個注釋,這個業(yè)務(wù)的拆分和方法的封裝。一個大業(yè)務(wù)里面有多個小業(yè)務(wù),不同的業(yè)務(wù)調(diào)用不同的 service 方法即可,后續(xù)接手的人即使沒有流程圖等相關(guān)文檔也能快速理解這里的業(yè)務(wù),而很多初級開發(fā)寫出來的業(yè)務(wù)方法就是上一行代碼是 A 業(yè)務(wù)的,下一行代碼是 B業(yè)務(wù)的,在下面一行代碼又是 A 業(yè)務(wù)的,業(yè)務(wù)調(diào)用之間還嵌套這一堆單元邏輯,顯得非常混亂,代碼還多。

          判斷集合類型不為空的正確方式

          很多人喜歡寫這樣的代碼去判斷集合

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

          當然你硬要這么寫也沒什么問題......但是不覺得難受么,現(xiàn)在框架中隨便一個 jar 包都有集合工具類,比如?org.springframework.util.CollectionUtilscom.baomidou.mybatisplus.core.toolkit.CollectionUtils?。以后請這么寫

          if (CollectionUtils.isEmpty(list) || CollectionUtils.isNotEmpty(list)) {  return null;}

          集合類型返回值不要 return null

          當你的業(yè)務(wù)方法返回值是集合類型時,請不要返回 null,正確的操作是返回一個空集合。你看 mybatis 的列表查詢,如果沒查詢到元素返回的就是一個空集合,而不是 null。否則調(diào)用方得去做 NULL 判斷,多數(shù)場景下對于對象也是如此。

          映射數(shù)據(jù)庫的屬性盡量不要用基本類型

          我們都知道 int/long 等基本數(shù)據(jù)類型作為成員變量默認值是 0。現(xiàn)在流行使用 mybatisplus 、mybatis 等 ORM 框架,在進行插入或者更新的時候很容易會帶著默認值插入更新到數(shù)據(jù)庫。我特么真想砍了之前的開發(fā),重構(gòu)的項目里面實體類里面全都是基本數(shù)據(jù)類型。當場裂開......

          封裝判斷條件

          public void method(LoanAppEntity loanAppEntity, long operatorId) {  if (LoanAppEntity.LoanAppStatus.OVERDUE != loanAppEntity.getStatus()          && LoanAppEntity.LoanAppStatus.CURRENT != loanAppEntity.getStatus()          && LoanAppEntity.LoanAppStatus.GRACE_PERIOD != loanAppEntity.getStatus()) {    //...    return;  }

          這段代碼的可讀性很差,這 if 里面誰知道干啥的?我們用面向?qū)ο蟮乃枷肴ソo?loanApp?這個對象里面封裝個方法不就行了么?

          public void method(LoanAppEntity loan, long operatorId) {  if (!loan.finished()) {    //...    return;  }

          LoanApp 這個類中封裝一個方法,簡單來說就是這個邏輯判斷細節(jié)不該出現(xiàn)在業(yè)務(wù)方法中。

          /** * 貸款單是否完成 */public boolean finished() {  return LoanAppEntity.LoanAppStatus.OVERDUE != this.getStatus()          && LoanAppEntity.LoanAppStatus.CURRENT != this.getStatus()          && LoanAppEntity.LoanAppStatus.GRACE_PERIOD != this.getStatus();}

          控制方法復(fù)雜度

          推薦一款 IDEA 插件?CodeMetrics?,它能顯示出方法的復(fù)雜度,它是對方法中的表達式進行計算,布爾表達式,if/else 分支,循環(huán)等。


          點擊可以查看哪些代碼增加了方法的復(fù)雜度,可以適當進行參考,畢竟我們通常寫的是業(yè)務(wù)代碼,在保證正常工作的前提下最重要的是要讓別人能夠快速看懂。當你的方法復(fù)雜度超過 10 就要考慮是否可以優(yōu)化了。

          使用 @ConfigurationProperties 代替 @Value

          之前居然還看到有文章推薦使用 @Value 比 @ConfigurationProperties 好用的,吐了,別誤人子弟。列舉一下 @ConfigurationProperties 的好處。

          • 在項目?application.yml?配置文件中按住 ctrl + 鼠標左鍵點擊配置屬性可以快速導(dǎo)航到配置類。寫配置時也能自動補全、聯(lián)想到注釋。需要額外引入一個依賴?org.springframework.boot:spring-boot-configuration-processor?。


          • @ConfigurationProperties 支持 NACOS 配置自動刷新,使用 @Value 需要在 BEAN 上面使用 @RefreshScope 注解才能實現(xiàn)自動刷新

          • @ConfigurationProperties 可以結(jié)合 Validation 校驗,@NotNull、@Length 等注解,如果配置校驗沒通過程序?qū)硬黄饋恚霸绲陌l(fā)現(xiàn)生產(chǎn)丟失配置等問題。

          • @ConfigurationProperties 可以注入多個屬性,@Value 只能一個一個寫

          • @ConfigurationProperties 可以支持復(fù)雜類型,無論嵌套多少層,都可以正確映射成對象

          相比之下我不明白為什么那么多人不愿意接受新的東西,裂開......你可以看下所有的 springboot-starter 里面用的都是 @ConfigurationProperties 來接配置屬性。

          推薦使用 lombok

          當然這是一個有爭議的問題,我的習(xí)慣是使用它省去?getter、setter、toString?等等。

          不要在 AService 調(diào)用 BMapper

          我們一定要遵循從?AService -> BService -> BMapper,如果每個 Service 都能直接調(diào)用其他的 Mapper,那特么還要其他 Service 干嘛?老項目還有從 controller 調(diào)用 mapper 的,把控制器當 service 來處理了。。。

          盡量少寫工具類

          為什么說要少寫工具類,因為你寫的大部分工具類,在你無形中引入的 jar 包里面就有,String 的,Assert 斷言的,IO 上傳文件,拷貝流的,Bigdecimal 的等等。自己寫容易錯還要加載多余的類。

          不要包裹 OpenFeign 接口返回值

          搞不懂為什么那么多人喜歡把接口的返回值用 Response 包裝起來......加個?code、message、success?字段,然后每次調(diào)用方就變成這樣

          CouponCommonResult bindResult = couponApi.useCoupon(request.getCustomerId(), order.getLoanId(), coupon.getCode());if (Objects.isNull(bindResult) || !bindResult.getResult()) {  throw new AppException(CouponErrorCode.ERR_REC_COUPON_USED_FAILED);}

          這樣就相當于

          1. 在 coupon-api 拋出異常

          2. 在 coupon-api 攔截異常,修改 Response.code

          3. 在調(diào)用方判斷 response.code 如果是 FAIELD 再把異常拋出去......

          你直接在服務(wù)提供方拋異常不就行了么。。。而且這樣一包裝 HTTP 請求永遠都是 200,沒法做重試和監(jiān)控。當然這個問題涉及到接口響應(yīng)體該如何設(shè)計,目前網(wǎng)上大多是三種流派

          • 接口響應(yīng)狀態(tài)一律 200

          • 接口響應(yīng)狀態(tài)遵從HTTP真實狀態(tài)

          • 佛系開發(fā),領(lǐng)導(dǎo)怎么說就怎么做

          不接受反駁,我推薦使用 HTTP 標準狀態(tài)。特定場景包括參數(shù)校驗失敗等一律使用 400 給前端彈 toast。下篇文章會闡述一律 200 的壞處。

          寫有意義的方法注釋

          這種注釋你寫出來是怕后面接手的人瞎么......

          /*** 請求電話驗證** @param credentialNum* @param callback* @param param* @return PhoneVerifyResult*/

          要么就別寫,要么就在后面加上描述......寫這樣的注釋被 IDEA 報一堆警告看著蛋疼

          和前端交互的 DTO 對象命名

          什么 VO、BO、DTO、PO 我倒真是覺得沒有那么大必要分那么詳細,至少我們在和前端交互的時候類名要起的合適,不要直接用映射數(shù)據(jù)庫的類返回給前端,這會返回很多不必要的信息,如果有敏感信息還要特殊處理。

          推薦的做法是接受前端請求的類定義為?XxxRequest,響應(yīng)的定義為?XxxResponse。以訂單為例:接受保存更新訂單信息的實體類可以定義為?OrderRequest,訂單查詢響應(yīng)定義為?OrderResponse,訂單的查詢條件請求定義為?OrderQueryRequest

          盡量別讓 IDEA 報警

          我是很反感看到 IDEA 代碼窗口一串警告的,非常難受。因為有警告就代表代碼還可以優(yōu)化,或者說存在問題。前幾天捕捉了一個團隊內(nèi)部的小bug,其實本來和我沒有關(guān)系,但是同事都在一頭霧水的看外面的業(yè)務(wù)判斷為什么走的分支不對,我一眼就掃到了問題。


          因為 java 中整數(shù)字面量都是?int?類型,到集合中就變成了?Integer,然后?stepId?點上去一看是?long?類型,在集合中就是?Long,那這個?contains?妥妥的返回?false,都不是一個類型。

          你看如果注重到警告,鼠標移過去看一眼提示就清楚了,少了一個生產(chǎn) bug。

          盡可能使用新技術(shù)組件

          我覺得這是一個程序員應(yīng)該具備的素養(yǎng)......反正我是喜歡用新的技術(shù)組件,因為新的技術(shù)組件出現(xiàn)必定是解決舊技術(shù)組件的不足,而且作為一個技術(shù)人員我們應(yīng)該要與時俱進~~ 當然前提是要做好準備工作,不能無腦升級。舉個最簡單的例子,Java 17 都出來了,新項目現(xiàn)在還有人用 Date 來處理日期時間...... 都什么年代了你還在用 Date

          結(jié)語

          本篇文章簡單介紹我日常開發(fā)的習(xí)慣,當然僅是作者自己的見解。暫時只想到這幾點,以后發(fā)現(xiàn)其他的會更新。

          如果這篇文章對你有幫助,記得點贊加關(guān)注!你的支持就是我繼續(xù)創(chuàng)作的動力!


          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  超碰网站在线 | 国产播放在线 | 人人干人人妻 | 欧美性爱内射大奶美女 | 亚洲高清av|