夜雨聆风学习资料网

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 {    @Override    protectedvoidreadData() {        System.out.println("Reading data from CSV file.");    }    @Override    protectedvoidprocessData() {        System.out.println("Processing CSV data.");    }}// JSON 处理器class JSONDataProcessor extends DataProcessor {    @Override    protectedvoidreadData() {        System.out.println("Reading data from JSON file.");    }    @Override    protectedvoidprocessData() {        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 {    @Override    publicvoidreadData() {        System.out.println("Reading data from CSV file.");    }    @Override    publicvoidprocessData() {        System.out.println("Processing CSV data.");    }}class JSONDataProcessor implements DataProcessor {    @Override    publicvoidreadData() {        System.out.println("Reading data from JSON file.");    }    @Override    publicvoidprocessData() {        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();        // CSV        template.process(                () -> System.out.println("Reading data from CSV file."),                () -> System.out.println("Processing CSV data.")        );        // JSON        template.process(                () -> System.out.println("Reading data from JSON file."),                () -> System.out.println("Processing JSON data.")        );    }}

三种方案对比 & 面试口述

1.抽象类继承:正统模板方法;缺点:受 Java 单继承约束,一旦业务类已有父类,无法使用。

2.接口 default:上手简单,不占继承名额;风险:default 方法没有 final,容易被重写,多接口容易冲突。

3.组合 + 回调:工程首选,Spring 大量使用;骨架固定,通过 Lambda 传入可变逻辑,完美避开单继承限制。

一句话总结:模板方法的核心本质是「固定流程骨架,开放自定义步骤」;继承只是实现手段,不是必须。当继承这条路走不通的时候,优先换成组合回调。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料