← 返回题目列表

如何把重复流程代码重构成模板方法模式?

高频 中等 第 13 / 26 题 更新于 2026/08/02
模板方法模式重构代码复用重复流程

简化版

重构成模板方法模式的步骤是:先找多个类中重复的流程骨架,再区分固定步骤和变化步骤,把固定流程上移到抽象父类,把变化步骤抽成 protected abstract 或钩子方法,模板入口用 final 固定顺序,最后补子类契约测试。不要只因为代码重复就抽模板,流程顺序稳定、变化点清晰才适合。

详细版

假设 CSV、Excel、JSON 三种导入类都有类似流程:

校验文件 -> 解析数据 -> 校验数据 -> 保存数据 -> 记录日志

只有“解析数据”的方式不同。此时可以抽出:

abstract class AbstractImporter {
    public final void importFile(File file) {
        validateFile(file);
        List<Row> rows = parse(file);
        validateRows(rows);
        save(rows);
        audit(file, rows);
    }

    protected abstract List<Row> parse(File file);
}

CSV、Excel、JSON 子类只实现 parse。重构后的收益是流程一致、重复代码减少、关键步骤不会漏。

记忆钩子:重构模板方法不是抽公共代码,而是抽稳定流程。

完整版教学

面试提示:重构题要先找重复流程骨架,再抽出稳定步骤和变化步骤,别一上来就继承。

一、先找重复流程而不是重复语句

模板方法适合重复流程:

A -> B -> C -> D

不是看到两行代码相同就抽父类。流程顺序稳定,才值得用模板方法。

例如导入、导出、批处理、报表生成、爬取任务都常见重复流程。

二、标记固定步骤和变化步骤

可以列一张表:

步骤是否固定处理方式
校验文件固定父类 private
解析数据变化子类 abstract
校验数据固定父类 private
保存数据固定或可变看业务
后置通知可选hook

这个过程比直接写代码更重要。

三、把固定流程上移到父类

父类控制顺序:

public abstract class AbstractImportTemplate {
    public final ImportResult importFile(File file) {
        checkFile(file);
        List<Row> rows = parse(file);
        checkRows(rows);
        ImportResult result = save(rows);
        afterImport(result);
        return result;
    }
}

模板入口命名要表达业务动作,不要叫过于抽象的 execute,除非上下文非常明确。

四、把变化点设计成最小扩展面

只把确实变化的部分留给子类:

protected abstract List<Row> parse(File file);

protected void afterImport(ImportResult result) {
    // default no-op
}

不要一次开放太多 protected 方法,否则父类很快失控。

五、迁移子类时保持行为一致

重构前后要保证外部行为不变。可以先写 characterization test:

given same csv file
old importer result == new importer result

然后逐个迁移子类,避免一次改太多。

六、重构后要删除重复入口

如果子类仍然保留各自入口:

csvImport.importCsv();
excelImport.importExcel();

外部可能继续绕过父类。最好统一公共入口,保留适配方法也要委托到模板入口。

七、不适合重构成模板的情况

不适合的信号:

  • 流程顺序经常变;
  • 每个子类都要覆盖大部分步骤;
  • 子类之间不是同一类业务;
  • 需要运行时切换算法;
  • 组合比继承更自然。

这些场景可考虑策略、组合或流程编排。

八、常见误区与追问

  • 误区:只要代码重复就抽模板方法。 关键看流程骨架是否稳定,而不是局部语句重复。
  • 误区:父类越抽象越好。 父类应表达清晰业务流程,过度抽象会变难懂。
  • 误区:所有差异都用钩子解决。 差异太多说明不适合一个模板。
  • 误区:重构后不用测旧行为。 模板方法会影响执行顺序,必须回归关键路径。
  • 追问:如何识别变化点? 对比多个实现,找顺序相同但实现不同的步骤。
  • 追问:重构过程中如何降低风险? 先测试旧行为,再抽父类,逐个迁移子类,小步提交。
  • 追问:如果只有两个类重复要不要抽? 看未来扩展和流程稳定度;重复少且变化少时可以先不抽。

九、加强记忆

  1. 抽模板方法先找稳定流程。
  2. 固定步骤上移父类,变化步骤留给子类。
  3. 模板入口 final 固定顺序。
  4. 扩展点越少越稳。
  5. 重构前后要用测试锁住行为。
  6. 差异太多时不要硬抽模板。