自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

利用 Function 打造高效斷言神器,真優(yōu)雅!

開發(fā)
本文將以一個實(shí)際應(yīng)用場景為例,即使用 Java 8 的函數(shù)式編程特性來重構(gòu)數(shù)據(jù)有效性斷言邏輯,展示如何通過 SFunction減少代碼重復(fù),從而提升代碼的優(yōu)雅性和可維護(hù)性。

在 Java 開發(fā)的征途中,我們時常與重復(fù)代碼不期而遇。這些重復(fù)代碼不僅讓項(xiàng)目顯得笨重,更增加了維護(hù)成本。幸運(yùn)的是,Java 8 帶來了函數(shù)式編程的春風(fēng),以 Function 接口為代表的一系列新特性,為我們提供了破除這一難題的利劍。

本文將以一個實(shí)際應(yīng)用場景為例,即使用 Java 8 的函數(shù)式編程特性來重構(gòu)數(shù)據(jù)有效性斷言邏輯,展示如何通過 SFunction(基于 Java 8 的 Lambda 表達(dá)式封裝)減少代碼重復(fù),從而提升代碼的優(yōu)雅性和可維護(hù)性。

一、背景故事:數(shù)據(jù)校驗(yàn)的煩惱

想象一下,在一個復(fù)雜的業(yè)務(wù)系統(tǒng)中,我們可能需要頻繁地驗(yàn)證數(shù)據(jù)庫中某個字段值是否有效,是否符合預(yù)期值。傳統(tǒng)的做法可能充斥著大量相似的查詢邏輯,每次都需要手動構(gòu)建查詢條件、執(zhí)行查詢并處理結(jié)果,這樣的代碼既冗長又難以維護(hù)。

例如以下兩個驗(yàn)證用戶 ID 和部門 ID 是否有效的方法,雖然簡單,但每次需要校驗(yàn)不同實(shí)體或不同條件時,就需要復(fù)制粘貼并做相應(yīng)修改,導(dǎo)致代碼庫中充滿了大量雷同的校驗(yàn)邏輯,給維護(hù)帶來了困擾。

// 判斷用戶 ID 是否有效
public void checkUserExistence(String userId) {
    User user = userDao.findById(userId);
    if (user == null) {
        thrownew RuntimeException('用戶ID無效');
    }
}

// 判斷部門 ID 是否有效
public void checkDeptExistence(String deptId) {
    Dept dept = deptDao.findById(deptId);
    if (dept == null) {
        thrownew RuntimeException('部門ID無效');
    }   
}

二、Java 8 的魔法棒:函數(shù)式接口

Java 8 引入了函數(shù)式接口的概念,其中 Function<T, R> 是最基礎(chǔ)的代表,它接受一個類型 T 的輸入,返回類型 R 的結(jié)果。而在 MyBatis Plus 等框架中常用的 SFunction 是對 Lambda 表達(dá)式的進(jìn)一步封裝,使得我們可以更加靈活地操作實(shí)體類的屬性。

三、實(shí)戰(zhàn)演練:重構(gòu)斷言方法

下面的 ensureColumnValueValid 方法正是利用了函數(shù)式接口的魅力,實(shí)現(xiàn)了對任意實(shí)體類指定列值的有效性斷言:

/**
 * 確認(rèn)數(shù)據(jù)庫字段值有效(通用)
 * 
 * @param <V> 待驗(yàn)證值的類型
 * @param valueToCheck 待驗(yàn)證的值
 * @param columnExtractor 實(shí)體類屬性提取函數(shù)
 * @param queryExecutor 單條數(shù)據(jù)查詢執(zhí)行器
 * @param errorMessage 異常提示信息模板
 */
publicstatic <T, R, V> void ensureColumnValueValid(V valueToCheck, SFunction<T, R> columnExtractor, SFunction<LambdaQueryWrapper<T>, T> queryExecutor, String errorMessage) {
    if (valueToCheck == null) return;
    
    LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(columnExtractor);
    wrapper.eq(columnExtractor, valueToCheck);
    wrapper.last('LIMIT 1');

    T entity = queryExecutor.apply(wrapper);
    R columnValue = columnExtractor.apply(entity);
    if (entity == null || columnValue == null)
        thrownew DataValidationException(String.format(errorMessage, valueToCheck));
}

這個方法接受一個待驗(yàn)證的值、一個實(shí)體類屬性提取函數(shù)、一個單行數(shù)據(jù)查詢執(zhí)行器和一個異常信息模板作為參數(shù)。通過這四個參數(shù),不僅能夠進(jìn)行針對特定屬性的有效性檢查,而且還能生成具有一致性的異常信息。

四、對比分析

使用 Function 改造前:

// 判斷用戶 ID 是否有效
public void checkUserExistence(String userId) {
    User user = userDao.findById(userId);
    if (user == null) {
        thrownew RuntimeException('用戶ID無效');
    }
}

// 判斷部門 ID 是否有效
public void checkDeptExistence(String deptId) {
    Dept dept = deptDao.findById(deptId);
    if (dept == null) {
        thrownew RuntimeException('部門ID無效');
    }   
}

使用 Function 改造后:

public void assignTaskToUser(AddOrderDTO dto) {
    ensureColumnValueValid(dto.getUserId(), User::getId, userDao::getOne, '用戶ID無效');
    ensureColumnValueValid(dto.getDeptId(), Dept::getId, deptDao::getOne, '部門ID無效');    
    ensureColumnValueValid(dto.getCustomerId(), Customer::getId, customerDao::getOne, '客戶ID無效');
    ensureColumnValueValid(dto.getDeptId(), Dept::getId, deptDao::getOne, '部門ID無效');
    ensureColumnValueValid(dto.getSupplieId(), Supplie::getId, supplierDao::getOne, '供應(yīng)商ID無效');

    // 現(xiàn)在可以確信客戶存在
    Customer cus = customerDao.findById(dto.getCustomerId());     
    
    // 創(chuàng)建訂單的邏輯...
}

對比上述兩段代碼,我們發(fā)現(xiàn)后者不僅大幅減少了代碼量,而且通過函數(shù)式編程,表達(dá)出更為清晰的邏輯意圖,可讀性和可維護(hù)性都有所提高。

優(yōu)點(diǎn):

  • 減少重復(fù)代碼: 通過 ensureColumnValueValid 方法,所有涉及數(shù)據(jù)庫字段值有效性檢查的地方都可以復(fù)用相同的邏輯,將變化的部分作為參數(shù)傳遞,大大減少了因特定校驗(yàn)邏輯而產(chǎn)生的代碼量。
  • 增強(qiáng)代碼復(fù)用: 抽象化的校驗(yàn)方法適用于多種場景,無論是用戶ID、訂單號還是其他任何實(shí)體屬性的校驗(yàn),一套邏輯即可應(yīng)對。
  • 提升可讀性和維護(hù)性: 通過清晰的函數(shù)簽名和 Lambda 表達(dá)式,代碼意圖一目了然,降低了后續(xù)維護(hù)的成本。
  • 靈活性和擴(kuò)展性: 當(dāng)校驗(yàn)規(guī)則發(fā)生變化時,只需要調(diào)整 ensureColumnValueValid 方法或其內(nèi)部實(shí)現(xiàn),所有調(diào)用該方法的地方都會自動受益,提高了系統(tǒng)的靈活性和擴(kuò)展性。

五、舉一反三:拓展校驗(yàn)邏輯的邊界

通過上述的實(shí)踐,我們見識到了函數(shù)式編程在簡化數(shù)據(jù)校驗(yàn)邏輯方面的威力。但這只是冰山一角,我們可以根據(jù)不同的業(yè)務(wù)場景,繼續(xù)擴(kuò)展和完善校驗(yàn)邏輯,實(shí)現(xiàn)更多樣化的校驗(yàn)需求。以下兩個示例展示了如何在原有基礎(chǔ)上進(jìn)一步深化,實(shí)現(xiàn)更復(fù)雜的數(shù)據(jù)比較和驗(yàn)證功能。

1. 斷言指定列值等于預(yù)期值

首先,考慮一個場景:除了驗(yàn)證數(shù)據(jù)的存在性,我們還需確認(rèn)查詢到的某列值是否與預(yù)期值相符。這在驗(yàn)證用戶角色、狀態(tài)變更等場景中尤為常見。為此,我們設(shè)計了 validateColumnValueMatchesExpected 方法:

/**
 * 驗(yàn)證查詢結(jié)果中指定列的值是否與預(yù)期值匹配
 *
 * @param <T>             實(shí)體類型
 * @param <R>             目標(biāo)列值的類型
 * @param <C>             查詢條件列值的類型
 * @param targetColumn    目標(biāo)列的提取函數(shù),用于獲取想要驗(yàn)證的列值
 * @param expectedValue   期望的列值
 * @param conditionColumn 條件列的提取函數(shù),用于設(shè)置查詢條件
 * @param conditionValue  條件列對應(yīng)的值
 * @param queryMethod     執(zhí)行查詢的方法引用,返回單個實(shí)體對象
 * @param errorMessage    驗(yàn)證失敗時拋出異常的錯誤信息模板
 * @throws RuntimeException 當(dāng)查詢結(jié)果中目標(biāo)列的值與預(yù)期值不匹配時拋出異常
 */
publicstatic <T, R, C> void validateColumnValueMatchesExpected(
      SFunction<T, R> targetColumn, R expectedValue,
      SFunction<T, C> conditionColumn, C conditionValue,
      SFunction<LambdaQueryWrapper<T>, T> queryMethod,
      String errorMessage) {

   // 創(chuàng)建查詢包裝器,選擇目標(biāo)列并設(shè)置查詢條件
   LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
   wrapper.select(targetColumn);
   wrapper.eq(conditionColumn, conditionValue);

   // 執(zhí)行查詢方法
   T one = queryMethod.apply(wrapper);
   // 如果查詢結(jié)果為空,則直接返回,視為驗(yàn)證通過(或忽略)
   if (one == null) return;

   // 獲取查詢結(jié)果中目標(biāo)列的實(shí)際值
   R actualValue = targetColumn.apply(one);

   // 比較實(shí)際值與預(yù)期值是否匹配,這里假設(shè)notMatch是一個自定義方法用于比較不匹配情況
   boolean doesNotMatch = notMatch(actualValue, expectedValue);
   if (doesNotMatch) {
      // 若不匹配,則根據(jù)錯誤信息模板拋出異常
      thrownew RuntimeException(String.format(errorMessage, expectedValue, actualValue));
   }
}

// 假設(shè)的輔助方法,用于比較值是否不匹配,根據(jù)實(shí)際需要實(shí)現(xiàn)
privatestatic <R> boolean notMatch(R actual, R expected) {
    // 示例簡單實(shí)現(xiàn)為不相等判斷,實(shí)際情況可能更復(fù)雜
    return !Objects.equals(actual, expected);
}

這個方法允許我們指定一個查詢目標(biāo)列(targetColumn)、預(yù)期值(expectedValue)、查詢條件列(conditionColumn)及其對應(yīng)的條件值(conditionValue),并提供一個查詢方法(queryMethod)來執(zhí)行查詢。如果查詢到的列值與預(yù)期不符,則拋出異常,錯誤信息通過 errorMessage 參數(shù)定制。

應(yīng)用場景:例如在一個權(quán)限管理系統(tǒng)中,當(dāng)需要更新用戶角色時,系統(tǒng)需要確保當(dāng)前用戶的角色在更新前是 “普通用戶”,才能將其升級為 “管理員”。此場景下,可以使用 validateColumnValueMatchesExpected 方法來驗(yàn)證用戶當(dāng)前的角色是否確實(shí)為“普通用戶”。

// 當(dāng)用戶角色不是 “普通用戶” 時拋異常
validateColumnValueMatchesExpected(User::getRoleType, '普通用戶', User::getId, userId, userMapper::getOne, '用戶角色不是普通用戶,無法升級為管理員!');

2. 斷言指定值位于期望值列表內(nèi)

進(jìn)一步,某些情況下我們需要驗(yàn)證查詢結(jié)果中的某一列值是否屬于一個預(yù)設(shè)的值集合。例如,驗(yàn)證用戶角色是否合法。為此,我們創(chuàng)建了 validateColumnValueMatchesExpectedList 方法:

/**
 * 驗(yàn)證查詢結(jié)果中指定列的值是否位于預(yù)期值列表內(nèi)
 *
 * @param <T>             實(shí)體類型
 * @param <R>             目標(biāo)列值的類型
 * @param <C>             查詢條件列值的類型
 * @param targetColumn    目標(biāo)列的提取函數(shù),用于獲取想要驗(yàn)證的列值
 * @param expectedValueList 期望值的列表
 * @param conditionColumn 條件列的提取函數(shù),用于設(shè)置查詢條件
 * @param conditionValue  條件列對應(yīng)的值
 * @param queryMethod     執(zhí)行查詢的方法引用,返回單個實(shí)體對象
 * @param errorMessage    驗(yàn)證失敗時拋出異常的錯誤信息模板
 * @throws RuntimeException 當(dāng)查詢結(jié)果中目標(biāo)列的值不在預(yù)期值列表內(nèi)時拋出異常
 */
publicstatic <T, R, C> void validateColumnValueInExpectedList(
        SFunction<T, R> targetColumn, List<R> expectedValueList,
        SFunction<T, C> conditionColumn, C conditionValue,
        SFunction<LambdaQueryWrapper<T>, T> queryMethod,
        String errorMessage) {

    LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
    wrapper.select(targetColumn);
    wrapper.eq(conditionColumn, conditionValue);

    T one = queryMethod.apply(wrapper);
    if (one == null) return;

    R actualValue = targetColumn.apply(one);
    if (actualValue == null) thrownew RuntimeException('列查詢結(jié)果為空');

    if (!expectedValueList.contains(actualValue)) {        
        thrownew RuntimeException(errorMessage);
    }
}

這個方法接受一個目標(biāo)列(targetColumn)、一個預(yù)期值列表(expectedValueList)、查詢條件列(conditionColumn)及其條件值(conditionValue),同樣需要一個查詢方法(queryMethod)。如果查詢到的列值不在預(yù)期值列表中,則觸發(fā)異常。

應(yīng)用場景: 在一個電商平臺的訂單處理流程中,系統(tǒng)需要驗(yàn)證訂單狀態(tài)是否處于可取消的狀態(tài)列表里(如 “待支付”、“待發(fā)貨”)才允許用戶取消訂單。此時,validateColumnValueInExpectedList 方法能有效確保操作的合法性。

// 假設(shè) OrderStatusEnum 枚舉了所有可能的訂單狀態(tài),cancelableStatuses 包含可取消的狀態(tài)
List<String> cancelableStatuses = Arrays.asList(OrderStatusEnum.WAITING_PAYMENT.getValue(), OrderStatusEnum.WAITING_DELIVERY.getValue());

// 驗(yàn)證訂單狀態(tài)是否在可取消狀態(tài)列表內(nèi)
validateColumnValueInExpectedList(Order::getStatus, cancelableStatuses, Order::getOrderId, orderId, orderMapper::selectOne, '訂單當(dāng)前狀態(tài)不允許取消!');

通過這兩個擴(kuò)展方法,我們不僅鞏固了函數(shù)式編程在減少代碼重復(fù)、提升代碼靈活性方面的優(yōu)勢,還進(jìn)一步證明了通過抽象和泛型設(shè)計,可以輕松應(yīng)對各種復(fù)雜的業(yè)務(wù)校驗(yàn)需求,使代碼更加貼近業(yè)務(wù)邏輯,易于理解和維護(hù)。

六、核心優(yōu)勢

(1) 代碼復(fù)用: 通過泛型和函數(shù)式接口,該方法能夠適應(yīng)任何實(shí)體類和屬性的校驗(yàn)需求,大大減少了重復(fù)的查詢邏輯代碼。

(2) 清晰表達(dá)意圖: 方法簽名直觀表達(dá)了校驗(yàn)邏輯的目的,提高了代碼的可讀性和可維護(hù)性。

(3) 靈活性: 使用者只需提供幾個簡單的 Lambda 表達(dá)式,即可完成復(fù)雜的查詢邏輯配置,無需關(guān)心底層實(shí)現(xiàn)細(xì)節(jié)。

易于維護(hù)與擴(kuò)展:

  • 當(dāng)需要增加新的實(shí)體驗(yàn)證時,僅需調(diào)用 ensureColumnValueValid 并傳入相應(yīng)的參數(shù),無需編寫新的驗(yàn)證邏輯,降低了維護(hù)成本。
  • 修改驗(yàn)證規(guī)則時,只需調(diào)整 ensureColumnValueValid 內(nèi)部實(shí)現(xiàn),所有調(diào)用處自動遵循新規(guī)則,便于統(tǒng)一管理。
  • 異常處理集中于 ensureColumnValueValid 方法內(nèi)部,統(tǒng)一了異常拋出行為,避免了在多個地方處理相同的邏輯錯誤,減少了潛在的錯誤源。

七、函數(shù)式編程的力量

通過這個實(shí)例,我們見證了函數(shù)式編程在簡化代碼、提高抽象層次上的強(qiáng)大能力。在 Java 8 及之后的版本中,擁抱函數(shù)式編程思想,不僅能夠使我們的代碼更加簡潔、靈活,還能在一定程度上促進(jìn)代碼的正確性和可測試性。因此,無論是日常開發(fā)還是系統(tǒng)設(shè)計,都值得我們深入探索和應(yīng)用這一現(xiàn)代編程范式,讓代碼如魔法般優(yōu)雅而高效。

責(zé)任編輯:趙寧寧 來源: 碼猿技術(shù)專欄
相關(guān)推薦

2022-08-31 14:39:47

物聯(lián)網(wǎng)智慧城市大數(shù)據(jù)

2013-11-08 11:15:54

2025-01-27 00:48:12

Java 8代碼接口

2016-11-23 08:10:16

Android St JRebel調(diào)試神器

2025-02-24 09:30:00

日志系統(tǒng)系統(tǒng)開發(fā)

2010-06-23 11:41:00

高校企業(yè)高效數(shù)據(jù)中心

2009-05-05 13:19:53

戴爾高效企業(yè)

2010-02-22 15:00:47

2019-11-15 09:58:04

LinuxAsciinemapython

2019-12-12 09:30:31

工具代碼開發(fā)

2024-10-31 11:16:19

高并發(fā)并發(fā)集JDK

2025-02-20 14:43:29

CRM系統(tǒng)ruoyi-vue開源

2017-12-21 14:36:10

大數(shù)據(jù)健身智慧

2023-04-17 23:49:09

開發(fā)代碼Java

2014-10-27 14:09:01

華為

2010-05-12 15:39:49

IT運(yùn)維信息化建設(shè)北塔

2010-11-01 09:27:24

2014-10-14 12:51:22

騰訊云

2019-03-28 15:52:34

電子簽約SaaS上上簽

2012-04-26 16:18:57

SAP
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號