日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

畢業快三年了,前后也待過幾家公司,碰到各種各樣的同事。見識過各種各樣的代碼,優秀的、垃圾的、不堪入目的、看了想跑路的等等,所以這篇文章記錄一下一個優秀的后端 JAVA 開發應該有哪些好的開發習慣。

拆分合理的目錄結構

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

優秀的后端應該有哪些開發習慣?

 


優秀的后端應該有哪些開發習慣?

 

等以后開發版本迭代,如果某個包可以繼續拆領域就繼續往下拆,可以很清楚的一覽項目業務模塊。后續拆微服務也簡單。

封裝方法形參

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

public void updateCustomerDeviceAndInstallInfo(long customerId, String channelKey,
                   String AndroidId, String imei, String gaId,
                   String gcmPushToken, String instanceId) {}
復制代碼

寫個對象出來

public class CustomerDeviceRequest {
    private Long customerId;
    //省略屬性......
}
復制代碼

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

封裝業務邏輯

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

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

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

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

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

if (list == null || list.size() == 0) {
  return null;
}
復制代碼

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

if (CollectionUtils.isEmpty(list) || CollectionUtils.isNotEmpty(list)) {
  return null;
}
復制代碼

集合類型返回值不要 return null

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

映射數據庫的屬性盡量不要用基本類型

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

封裝判斷條件

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 里面誰知道干啥的?我們用面向對象的思想去給 loanApp 這個對象里面封裝個方法不就行了么?

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

LoanApp 這個類中封裝一個方法,簡單來說就是這個邏輯判斷細節不該出現在業務方法中。

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

控制方法復雜度

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

優秀的后端應該有哪些開發習慣?

 

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

使用 @ConfigurationProperties 代替 @Value

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

  • 在項目 application.yml 配置文件中按住 ctrl + 鼠標左鍵點擊配置屬性可以快速導航到配置類。寫配置時也能自動補全、聯想到注釋。需要額外引入一個依賴 org.springframework.boot:spring-boot-configuration-processor 。
優秀的后端應該有哪些開發習慣?

 

  • @ConfigurationProperties 支持 NACOS 配置自動刷新,使用 @Value 需要在 BEAN 上面使用 @RefreshScope 注解才能實現自動刷新
  • @ConfigurationProperties 可以結合 Validation 校驗,@NotNull、@Length 等注解,如果配置校驗沒通過程序將啟動不起來,及早的發現生產丟失配置等問題。
  • @ConfigurationProperties 可以注入多個屬性,@Value 只能一個一個寫
  • @ConfigurationProperties 可以支持復雜類型,無論嵌套多少層,都可以正確映射成對象

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

推薦使用 lombok

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

不要在 AService 調用 BMapper

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

盡量少寫工具類

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

不要包裹 OpenFeign 接口返回值

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

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. 在調用方判斷 response.code 如果是 FAIELD 再把異常拋出去......

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

  • 接口響應狀態一律 200
  • 接口響應狀態遵從HTTP真實狀態
  • 佛系開發,領導怎么說就怎么做

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

寫有意義的方法注釋

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

/**
* 請求電話驗證
*
* @param credentialNum
* @param callback
* @param param
* @return PhoneVerifyResult
*/
復制代碼

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

和前端交互的 DTO 對象命名

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

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

盡量別讓 IDEA 報警

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

優秀的后端應該有哪些開發習慣?

 

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

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

盡可能使用新技術組件

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

結語

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

如果這篇文章對你有幫助,記得點贊加關注!你的支持就是我繼續創作的動力!

 

作者:暮色妖嬈丶
鏈接:
https://juejin.cn/post/7072252275002966030

分享到:
標簽:后端
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定