简介
"用函数替换重复代码"是最常用的重构手法之一。通过将重复出现的代码片段提取为独立函数,可以提升代码复用性、降低维护成本。此重构特别适用于消除代码的坏味道"Duplicated Code"。
针对的症状(代码坏味道)
- 完全相同的代码块出现在多个位置
- 结构相似但变量名不同的代码片段
- 通过复制粘贴实现的相似功能
用函数替换重复代码的详细步骤
- 识别重复模式
- 使用代码对比工具或人工审查找到重复代码
- 确认重复代码的功能一致性
- 参数化差异点
- 识别需要变化的变量和常量
- 确定函数参数列表
- 创建新函数
- 使用明确反映功能的方法命名
- 处理返回值类型和异常情况
- 替换重复代码
- 用函数调用替换原始代码块
- 保持原有代码的接口兼容性
示例
重构前代码:
class ReportGenerator {
void generatePDFReport(Data data) {
// Header
System.out.println("Report Header");
System.out.println("Generated: " + new Date());
// Data section
for (Item item : data.getItems()) {
System.out.printf("| %-15s | %10.2f |%n", item.name(), item.value());
}
// Footer
System.out.println("Report Footer");
}
void generateHTMLReport(Data data) {
// Header
System.out.println("<header>Report Header</header>");
System.out.println("<time>" + new Date() + "</time>");
// Data section
System.out.println("<table>");
for (Item item : data.getItems()) {
System.out.printf("<tr><td>%s</td><td>%.2f</td></tr>%n", item.name(), item.value());
}
System.out.println("</table>");
// Footer
System.out.println("<footer>Report Footer</footer>");
}
}
重构步骤:
-
提取公共的Header生成逻辑:
private void generateHeader(String format) { if (format.equals("PDF")) { System.out.println("Report Header"); System.out.println("Generated: " + new Date()); } else { System.out.println("<header>Report Header</header>"); System.out.println("<time>" + new Date() + "</time>"); } } -
参数化报表内容生成:
private void generateContent(String format, Data data) { if (format.equals("PDF")) { for (Item item : data.getItems()) { System.out.printf("| %-15s | %10.2f |%n", item.name(), item.value()); } } else { System.out.println("<table>"); for (Item item : data.getItems()) { System.out.printf("<tr><td>%s</td><td>%.2f</td></tr>%n", item.name(), item.value()); } System.out.println("</table>"); } } -
统一报表生成入口:
public void generateReport(String format, Data data) { generateHeader(format); generateContent(format, data); generateFooter(format); } private void generateFooter(String format) { if (format.equals("PDF")) { System.out.println("Report Footer"); } else { System.out.println("<footer>Report Footer</footer>"); } }
练习
基础练习题
-
简单重复代码提取
// 重构前 class Calculator { void add(int a, int b) { System.out.println("Start calculation..."); System.out.println(a + " + " + b + " = " + (a + b)); } void multiply(int a, int b) { System.out.println("Start calculation..."); System.out.println(a + " * " + b + " = " + (a * b)); } }
进阶练习题
-
参数化模板方法
// 重构前 class DataExporter { String exportCSV(List<Data> dataList) { StringBuilder sb = new StringBuilder(); sb.append("name,age,score\n"); for (Data d : dataList) { sb.append(d.name).append(",") .append(d.age).append(",") .append(d.score).append("\n"); } return sb.toString(); } String exportHTML(List<Data> dataList) { StringBuilder sb = new StringBuilder(); sb.append("<table>\n<tr><th>Name</th><th>Age</th><th>Score</th></tr>\n"); for (Data d : dataList) { sb.append("<tr><td>").append(d.name).append("</td>") .append("<td>").append(d.age).append("</td>") .append("<td>").append(d.score).append("</td></tr>\n"); } sb.append("</table>"); return sb.toString(); } }
综合拓展练习题
-
复杂业务逻辑重构
// 重构前 class OrderProcessor { void processDomesticOrder(Order order) { validateAddress(order); checkInventory(order); applyDomesticTax(order); generateInvoice(order); notifyShippingDepartment(order); } void processInternationalOrder(Order order) { validateAddress(order); checkInventory(order); applyCustomsDuty(order); generateInvoice(order); notifyShippingDepartment(order); } }
代码审查要点
-
优点:
- 降低代码重复率
- 统一业务逻辑处理
- 提升可测试性
-
潜在问题:
- 避免过度抽象导致方法参数爆炸
- 注意提取函数的单一职责原则
- 保持合理的抽象层级