Skip to content

模板方法

更新: 6/22/2025 字数: 0 字 时长: 0 分钟

定义

模板方法模式 (Template Method Pattern) 定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。该模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,实现了代码复用和扩展控制。

角色

  • 抽象类 (AbstractClass):定义算法骨架和基本操作
  • 具体子类 (ConcreteClass):实现抽象类中的抽象操作
  • 客户端 (Client):调用模板方法执行算法

实现

数据导出框架

java
// 抽象导出类
public abstract class DataExporter {
    // 模板方法
    public final void exportData() {
        connectToDataSource();
        extractData();
        transformData();
        saveData();
        disconnect();
    }
    
    // 具体方法
    private void connectToDataSource() {
        System.out.println("连接数据源...");
    }
    
    private void disconnect() {
        System.out.println("断开数据源连接");
    }
    
    // 抽象方法(由子类实现)
    protected abstract void extractData();
    protected abstract void transformData();
    
    // 钩子方法(可选覆盖)
    protected void saveData() {
        System.out.println("默认保存到文件系统");
    }
}

// 数据库导出实现
public class DatabaseExporter extends DataExporter {
    @Override
    protected void extractData() {
        System.out.println("从数据库提取数据...");
    }
    
    @Override
    protected void transformData() {
        System.out.println("转换数据库数据为 CSV 格式...");
    }
    
    @Override
    protected void saveData() {
        System.out.println("保存数据库导出文件到 FTP 服务器");
    }
}

// API 导出实现
public class ApiExporter extends DataExporter {
    @Override
    protected void extractData() {
        System.out.println("从 API 接口提取 JSON 数据...");
    }
    
    @Override
    protected void transformData() {
        System.out.println("转换 JSON 数据为 XML 格式...");
    }
    
    // 使用默认 saveData 方法
}

// 文件系统导出实现
public class FileExporter extends DataExporter {
    @Override
    protected void extractData() {
        System.out.println("从文件系统读取数据文件...");
    }
    
    @Override
    protected void transformData() {
        System.out.println("转换文件数据为 Excel 格式...");
    }
    
    @Override
    protected void saveData() {
        System.out.println("保存 Excel 文件到云存储");
    }
}

// 使用示例
public class ExportDemo {
    public static void main(String[] args) {
        System.out.println("=== 数据库导出 ===");
        DataExporter dbExporter = new DatabaseExporter();
        dbExporter.exportData();
        
        System.out.println("\n=== API 导出 ===");
        DataExporter apiExporter = new ApiExporter();
        apiExporter.exportData();
        
        System.out.println("\n=== 文件导出 ===");
        DataExporter fileExporter = new FileExporter();
        fileExporter.exportData();
    }
}

饮料制作系统

java
// 抽象饮料类
public abstract class Beverage {
    // 模板方法
    public final void prepareBeverage() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
        stir();
    }
    
    // 具体方法
    private void boilWater() {
        System.out.println("烧开水");
    }
    
    private void pourInCup() {
        System.out.println("倒入杯子");
    }
    
    private void stir() {
        System.out.println("搅拌");
    }
    
    // 抽象方法(由子类实现)
    protected abstract void brew();
    protected abstract void addCondiments();
    
    // 钩子方法(可选覆盖)
    protected boolean customerWantsCondiments() {
        return true;
    }
}

// 茶实现
public class Tea extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡茶叶");
    }
    
    @Override
    protected void addCondiments() {
        System.out.println("添加柠檬");
    }
    
    @Override
    protected boolean customerWantsCondiments() {
        // 实际应用中可根据用户输入决定
        return Math.random() > 0.5; // 50% 概率添加调料
    }
}

// 咖啡实现
public class Coffee extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡咖啡粉");
    }
    
    @Override
    protected void addCondiments() {
        System.out.println("添加糖和牛奶");
    }
}

// 热巧克力实现
public class HotChocolate extends Beverage {
    @Override
    protected void brew() {
        System.out.println("溶解巧克力粉");
    }
    
    @Override
    protected void addCondiments() {
        System.out.println("添加棉花糖");
    }
    
    @Override
    protected boolean customerWantsCondiments() {
        return false; // 热巧克力不需要额外调料
    }
}

// 使用示例
public class BeverageDemo {
    public static void main(String[] args) {
        System.out.println("=== 制作茶 ===");
        Beverage tea = new Tea();
        tea.prepareBeverage();
        
        System.out.println("\n=== 制作咖啡 ===");
        Beverage coffee = new Coffee();
        coffee.prepareBeverage();
        
        System.out.println("\n=== 制作热巧克力 ===");
        Beverage hotChocolate = new HotChocolate();
        hotChocolate.prepareBeverage();
    }
}

优缺点

优点:

  1. 代码复用最大化
  2. 反向控制结构(好莱坞原则)
  3. 封装不变部分
  4. 扩展可变部分
  5. 减少代码重复
  6. 便于维护
  7. 符合开闭原则

缺点:

  1. 子类数量增加
  2. 可能违反里氏替换原则
  3. 模板方法难以维护(修改模板影响所有子类)
  4. 继承关系限制
  5. 可能导致方法爆炸
  6. 调试困难

应用场景

  1. 框架设计(Spring, Hibernate)
  2. 算法骨架固定但步骤可变
  3. 工作流引擎
  4. 报表生成系统
  5. 数据导入/导出框架
  6. 单元测试框架(JUnit)
  7. 游戏引擎(渲染流程)
  8. 编译过程(词法分析→语法分析→语义分析)

与其他模式的关系

  • 与策略模式:模板方法使用继承,策略使用委托
  • 与工厂方法模式:工厂方法是模板方法的特例
  • 与装饰器模式:都可扩展行为,但机制不同
  • 与访问者模式:都分离算法与结构
  • 与桥接模式:模板方法侧重步骤,桥接侧重抽象/实现分离
  • 与命令模式:命令对象可封装模板方法调用

JDK 中的模板方法模式

  1. Java InputStream/OutputStream
  2. Java AbstractList/AbstractSet
  3. Java HttpServlet(service() 方法)
  4. Java AWT Component(paint() 方法)
  5. JUnit TestCase(setUp(), tearDown())
  6. Spring JdbcTemplate(execute())
  7. Java Thread(run() 方法)
  8. Java Collections.sort()

注意事项

  1. 模板方法应声明为 final
  2. 合理使用钩子方法
  3. 控制抽象方法数量
  4. 避免过度继承
  5. 考虑组合替代继承
  6. 模板方法复杂度控制
  7. 文档化算法步骤
  8. 处理异常策略

贡献者

The avatar of contributor named as wkwbk wkwbk
The avatar of contributor named as LI SIR LI SIR

页面历史