首页 > 其他分享 >利用 Lambda 获取更简洁的代码

利用 Lambda 获取更简洁的代码

时间:2024-03-23 16:59:26浏览次数:19  
标签:product 简洁 handle 代码 SQLException void processMessage throws Lambda

本文演示了一个 Java 重构的真实示例,旨在实现更简洁的代码和更好的关注点分离。这个想法源于我在专业环境中编码的经验。

从前在生产代码中
当我处理持久化一些域数据的代码时,我最终得到了以下结果:

public void processMessage(InsuranceProduct product) throws Exception {
    for (int retry = 0; retry <= MAX_RETRIES; retry++) {
        try {
            upsert(product);
            return;
        } catch (SQLException ex) {
            if (retry >= MAX_RETRIES) {
                throw ex;
            }
            LOG.warn("Fail to execute database update. Retrying...", ex);
            reestablishConnection();
        }
    }
}

private void upsert(InsuranceProduct product) throws SQLException {
    //content not relevant
}

它是框架协定的一部分,被调用以保留每个已处理的消息。该代码执行幂等数据库更新插入,并在发生错误时处理重试逻辑。我担心的主要错误是需要重新建立的超时JDBC连接。processMessage

从干净的代码角度来看,我对初始版本不满意。我期望一些东西可以立即揭示其意图,而无需深入研究代码。该方法充满了低级细节,需要了解才能知道它的作用。此外,我想将重试逻辑与重试的数据库操作分开,以便于重用。processMessage

我决定重写它以解决上述问题。

程序性更强,声明性更强
第一步是将调用移动到 lambda 驱动的变量。让 IDE 通过使用 Introduce Functional Variable 重构来帮助实现此目的。很不幸,我们收到一条错误消息:updateDatabase()
造成这种情况的原因是缺少提供与更新插入方法兼容的 SAM 接口的功能接口。为了解决这个问题,我们需要定义一个自定义函数接口,该接口声明一个不接受任何参数、不返回任何参数并抛出 .以下是我们需要提供的接口:SQLException

@FunctionalInterface
interface SqlRunnable {
    void run() throws SQLException;
}

自定义功能接口到位后,让我们重复重构。这一次,它成功了。另外,让我们将变量赋值移到 for 循环之前:

public void processMessage(InsuranceProduct product) throws Exception {
    final SqlRunnable handle = () -> upsert(product);
    for (int retry = 0; retry <= MAX_RETRIES; retry++) {
        try {
            handle.run();
            return;
        } catch (SQLException ex) {
            if (retry >= MAX_RETRIES) {
                throw ex;
            }
            LOG.warn("Fail to execute database update. Retrying...", ex);
            reestablishConnection();
        }
    }
}

使用 Extract 方法重构将 for 循环及其内容移动到名为 :retryOnSqlException

public void processMessage(InsuranceProduct product) throws Exception {
    final SqlRunnable handle = () -> upsert(product);
    retryOnSqlException(handle);
}

private void retryOnSqlException(SqlRunnable handle) throws SQLException {
    //skipped for clarity
}

最后一步是使用 Inline Variable 重构来内联变量。handle

最终结果如下。

public void processMessage(InsuranceProduct product) throws Exception {
    retryOnSqlException(() -> upsert(product));
}

框架入口方法现在清楚地说明了它正在做什么。它只有一行长,所以没有认知负荷。

支持代码包含有关它如何履行其职责并实现可重用性的详细信息:

private void retryOnSqlException(SqlRunnable handle) throws SQLException {
    for (int retry = 0; retry <= MAX_RETRIES; retry++) {
        try {
            handle.run();
            return;
        } catch (SQLException ex) {
            if (retry >= MAX_RETRIES) {
                throw ex;
            }
            LOG.warn("Fail to execute database update. Retrying...", ex);
            reestablishConnection();
        }
    }
}

@FunctionalInterface
interface SqlRunnable {
    void run() throws SQLException;
}

结论
值得付出努力吗?绝对。让我们总结一下好处。

该方法现在通过使用具有高级代码的声明性方法清楚地表达其意图。重试逻辑与数据库操作分离,并放置在其自己的方法中,由于命名良好,该方法可以精确地揭示其意图。此外,Lambda 语法允许将重试功能轻松重用到其他数据库操作中。processMessage

标签:product,简洁,handle,代码,SQLException,void,processMessage,throws,Lambda
From: https://blog.csdn.net/QWQ123Q/article/details/136888305

相关文章