本文演示了一个 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