ARTICLE · 1342808
Java 模板方法模式:撞上单继承限制,三种落地方案
Java 模板方法模式:撞上单继承限制,三种落地方案
面试经典题:如果一个类已经继承了其它类,还想使用模板方法模式,怎么办?
原生模板方法依靠抽象类 + 继承实现,但是 Java 是单继承,一旦业务类已经继承别的父类,这条路就堵死了。下面依次给出三种完整可运行的实现方案。
方案一:传统抽象类继承(原生模板方法)
这是最经典的模板方法实现:把固定流程封装在final的模板方法中,可变步骤定义成抽象方法,交由子类实现。 缺点:强依赖继承,占用唯一继承名额。
// 抽象类abstract class DataProcessor {// 模板方法,定死执行顺序,final 禁止子类篡改流程骨架public final voidprocess() {readData();processData();writeData();}protectedabstractvoidreadData(); // 子类必须实现protectedabstractvoidprocessData(); // 子类必须实现protectedvoidwriteData() { // 默认实现,子类可覆盖System.out.println("Writing data to output.");}}// CSV 处理器class CSVDataProcessor extends DataProcessor {@OverrideprotectedvoidreadData() {System.out.println("Reading data from CSV file.");}@OverrideprotectedvoidprocessData() {System.out.println("Processing CSV data.");}}// JSON 处理器class JSONDataProcessor extends DataProcessor {@OverrideprotectedvoidreadData() {System.out.println("Reading data from JSON file.");}@OverrideprotectedvoidprocessData() {System.out.println("Processing JSON data.");}}public class TemplateMain {publicstaticvoidmain(String[] args) {DataProcessor csv = new CSVDataProcessor();csv.process();DataProcessor json = new JSONDataProcessor();json.process();}}
方案二:接口 default 实现(Java8+)
利用 Java8 接口允许default默认方法的特性,把模板流程放在接口里面。业务类只需要implements这个接口,不占用继承名额;一个类可以同时实现多个接口。
注意:接口里面的默认方法不能标记为 final;如果多个接口存在同名 default 方法,会出现菱形继承冲突,需要手动解决。
interface DataProcessor {// default:模板骨架defaultvoidprocess() {readData();processData();writeData();}voidreadData();voidprocessData();defaultvoidwriteData() {System.out.println("Writing data to output.");}}class CSVDataProcessor implements DataProcessor {@OverridepublicvoidreadData() {System.out.println("Reading data from CSV file.");}@OverridepublicvoidprocessData() {System.out.println("Processing CSV data.");}}class JSONDataProcessor implements DataProcessor {@OverridepublicvoidreadData() {System.out.println("Reading data from JSON file.");}@OverridepublicvoidprocessData() {System.out.println("Processing JSON data.");}}public class TemplateMain {publicstaticvoidmain(String[] args) {DataProcessor csv = new CSVDataProcessor();csv.process();DataProcessor json = new JSONDataProcessor();json.process();}}
方案三:组合 + 回调(生产最常用,Spring Template 同款)
不再使用继承。模板骨架独立成普通类,可变步骤抽为函数式回调,调用的时候把自定义逻辑传入。 也就是JdbcTemplate / RedisTemplate的设计思路,优先组合、规避继承限制,也是项目里首选方案。
@FunctionalInterfaceinterface ReadCallback {voidreadData();}@FunctionalInterfaceinterface ProcessCallback {voidprocessData();}/*** 模板类:持有固定执行骨架*/class DataProcessorTemplate {public final voidprocess(ReadCallback readCallback, ProcessCallback processCallback) {readCallback.readData();processCallback.processData();writeData();}protectedvoidwriteData() {System.out.println("Writing data to output.");}}public class TemplateMain {publicstaticvoidmain(String[] args) {DataProcessorTemplate template = new DataProcessorTemplate();// CSVtemplate.process(() -> System.out.println("Reading data from CSV file."),() -> System.out.println("Processing CSV data."));// JSONtemplate.process(() -> System.out.println("Reading data from JSON file."),() -> System.out.println("Processing JSON data."));}}
三种方案对比 & 面试口述
1.抽象类继承:正统模板方法;缺点:受 Java 单继承约束,一旦业务类已有父类,无法使用。
2.接口 default:上手简单,不占继承名额;风险:default 方法没有 final,容易被重写,多接口容易冲突。
3.组合 + 回调:工程首选,Spring 大量使用;骨架固定,通过 Lambda 传入可变逻辑,完美避开单继承限制。
一句话总结:模板方法的核心本质是「固定流程骨架,开放自定义步骤」;继承只是实现手段,不是必须。当继承这条路走不通的时候,优先换成组合回调。















































































