在Java中如何使用抽象方法设计模板_JavaTemplateMethod设计解析

抽象方法必须定义在抽象类中且无方法体,不能被private、static或final修饰;模板方法模式核心是final模板方法定义算法骨架,抽象钩子方法供子类定制。

抽象方法必须定义在抽象类里,且子类必须实现

Java 中的 abstract 方法不能有方法体,也不能被 privatestaticfinal 修饰。它只声明行为契约,强制子类提供具体逻辑。

  • 如果父类含 abstract 方法,该类必须用 abstract class 声明,否则编译报错:error: abstract method in non-abstract class
  • 子类继承后,要么实现所有抽象方法,要么自己也声明为 abstract
  • 接口中也可以定义抽象方法(默认就是 public abstrac

    t
    ),但模板方法模式通常依赖抽象类的继承链,所以优先用抽象类

模板方法模式的核心:final 模板方法 + 抽象钩子方法

模板方法模式不是靠“抽象方法”本身实现复用,而是靠一个 final 的非抽象方法(即模板方法)来定义算法骨架,再把可变步骤拆成 abstractprotected 钩子方法供子类定制。

abstract class DataProcessor {
    // 模板方法:定义流程,不可重写
    public final void process() {
        loadData();
        validateData();
        transformData();
        saveData();
    }

    // 这些是抽象钩子,由子类决定怎么执行
    protected abstract void loadData();
    protected abstract void validateData();
    protected abstract void transformData();
    protected abstract void saveData();
}
  • process()final 的——防止子类破坏整体流程顺序
  • 四个 protected abstract 方法是“空缺”,子类必须填;也可提供默认空实现(变成 protected void xxx() {}),让子类选择性覆盖
  • 注意访问修饰符:用 protected 而非 public,避免外部直接调用钩子方法破坏封装

常见错误:把模板方法也做成 abstract,或漏掉 final

有人误以为“模板方法”就该是抽象的,结果写出这样的代码:

abstract class BadTemplate {
    // ❌ 错误:模板方法本身抽象,子类无法复用流程
    public abstract void execute(); 

    protected abstract void step1();
    protected abstract void step2();
}
  • 这样每个子类都要重写整个流程,完全失去“模板”的意义
  • 另一类错误是忘了加 final,导致子类可能覆盖 process(),绕过校验或跳过关键步骤
  • 还有一种隐蔽问题:在模板方法里调用了 this.xxx(),而 xxx() 是子类重写的非抽象方法——此时会触发子类版本(多态),但若子类还没初始化完,可能引发 NullPointerException

实际使用时,优先考虑组合优于继承

模板方法模式本质是基于继承的控制反转(IoC),但 Java 8+ 后更推荐用函数式接口 + 组合方式替代深度继承:

class DataProcessor {
    private final Supplier loader;
    private final Predicate validator;
    private final Function transformer;
    private final Consumer saver;

    public DataProcessor(Supplier loader,
                         Predicate validator,
                         Function transformer,
                         Consumer saver) {
        this.loader = loader;
        this.validator = validator;
        this.transformer = transformer;
        this.saver = saver;
    }

    public void process() {
        Data data = loader.get();
        if (validator.test(data)) {
            saver.accept(transformer.apply(data));
        }
    }
}
  • 这种方式更灵活,测试更容易(可传入 mock 函数),也不受单继承限制
  • 抽象类模板适合“强生命周期约束”的场景(如框架回调、资源必须按序初始化/销毁)
  • 真正需要模板方法的地方,往往是框架层(如 Spring 的 JdbcDaoSupport、JUnit 的 TestCase)——业务代码里过度使用容易导致类爆炸和紧耦合
抽象方法只是语法机制,模板方法模式的关键在于「流程固化 + 点位开放」。最容易被忽略的是:钩子方法的粒度设计——太粗(比如整个 transform())会让子类难以复用;太细(比如拆成 beforeTransform()doTransform()afterTransform())又让子类负担过重。得根据变化频率和正交性来权衡。