Java模板方法模式案例

wen java案例 2

Java模板方法模式案例

什么是模板方法模式?

模板方法模式是一种行为设计模式,它在一个方法中定义算法的骨架,而将一些步骤延迟到子类中实现,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

Java模板方法模式案例

模式结构

┌─────────────────┐
│   AbstractClass  │
├─────────────────┤
│ +templateMethod()│───► 定义算法骨架
│ +step1()         │
│ +step2()         │
│ +step3()         │
└─────────────────┘
         ▲
         │
┌────────┴────────┐
│  ConcreteClass  │
├─────────────────┤
│ +step1()        │
│ +step2()        │
│ +step3()        │
└─────────────────┘

实际案例:咖啡和茶的制作

1 基础抽象类

public abstract class Beverage {
    // 模板方法 - 定义制作饮料的算法骨架
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }
    // 具体方法 - 所有子类共享的步骤
    private void boilWater() {
        System.out.println("煮沸水");
    }
    private void pourInCup() {
        System.out.println("倒入杯中");
    }
    // 抽象方法 - 子类必须实现
    protected abstract void brew();
    protected abstract void addCondiments();
}

2 具体实现类 - 咖啡

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

3 具体实现类 - 茶

public class Tea extends Beverage {
    @Override
    protected void brew() {
        System.out.println("浸泡茶叶");
    }
    @Override
    protected void addCondiments() {
        System.out.println("添加柠檬");
    }
}

增强版 - 带钩子方法的模板模式

public abstract class BeverageWithHook {
    // 模板方法
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        // 钩子方法 - 让子类决定是否执行
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }
    private void boilWater() {
        System.out.println("煮沸水");
    }
    private void pourInCup() {
        System.out.println("倒入杯中");
    }
    protected abstract void brew();
    protected abstract void addCondiments();
    // 钩子方法 - 默认返回true,子类可以覆盖
    protected boolean customerWantsCondiments() {
        return true;
    }
}
// 咖啡实现
public class CoffeeWithHook extends BeverageWithHook {
    @Override
    protected void brew() {
        System.out.println("冲泡咖啡粉");
    }
    @Override
    protected void addCondiments() {
        System.out.println("添加糖和牛奶");
    }
    // 覆盖钩子方法
    @Override
    protected boolean customerWantsCondiments() {
        return askUser();
    }
    private boolean askUser() {
        System.out.print("是否要添加调料?(y/n): ");
        Scanner scanner = new Scanner(System.in);
        String answer = scanner.nextLine();
        return answer.toLowerCase().startsWith("y");
    }
}

更复杂的案例 - 数据迁移框架

// 数据迁移模板
public abstract class DataMigration {
    // 模板方法 - 定义迁移流程
    public final void migrate() {
        backup();
        validateSource();
        extractData();
        transformData();
        loadData();
        cleanup();
    }
    protected void backup() {
        System.out.println("执行数据备份...");
        // 通用备份逻辑
    }
    protected void cleanup() {
        System.out.println("清理临时资源...");
        // 通用清理逻辑
    }
    protected abstract void validateSource();
    protected abstract void extractData();
    protected abstract void transformData();
    protected abstract void loadData();
}
// MySQL到Oracle的迁移
public class MySqlToOracleMigration extends DataMigration {
    @Override
    protected void validateSource() {
        System.out.println("验证MySQL数据完整性和约束...");
    }
    @Override
    protected void extractData() {
        System.out.println("从MySQL提取数据...");
        // MySQL特定的提取逻辑
    }
    @Override
    protected void transformData() {
        System.out.println("转换数据类型:MySQL类型 -> Oracle类型...");
        // 数据类型映射
    }
    @Override
    protected void loadData() {
        System.out.println("批量加载数据到Oracle...");
        // Oracle批量插入逻辑
    }
}
// 文件到MongoDB的迁移
public class FileToMongoMigration extends DataMigration {
    @Override
    protected void backup() {
        // 覆盖默认备份
        System.out.println("文件迁移不需要备份,跳过");
    }
    @Override
    protected void validateSource() {
        System.out.println("检查文件格式和编码...");
    }
    @Override
    protected void extractData() {
        System.out.println("读取文件内容...");
    }
    @Override
    protected void transformData() {
        System.out.println("转换为BSON文档格式...");
    }
    @Override
    protected void loadData() {
        System.out.println("写入MongoDB...");
    }
}

测试代码

public class TemplateMethodDemo {
    public static void main(String[] args) {
        System.out.println("=== 咖啡制作 ===");
        Beverage coffee = new Coffee();
        coffee.prepareRecipe();
        System.out.println("\n=== 茶制作 ===");
        Beverage tea = new Tea();
        tea.prepareRecipe();
        System.out.println("\n=== 带钩子的咖啡制作 ===");
        BeverageWithHook coffeeWithHook = new CoffeeWithHook();
        coffeeWithHook.prepareRecipe();
        System.out.println("\n=== 数据迁移 ===");
        DataMigration migration = new MySqlToOracleMigration();
        migration.migrate();
        System.out.println("\n=== 文件迁移 ===");
        DataMigration fileMigration = new FileToMongoMigration();
        fileMigration.migrate();
    }
}

输出结果

=== 咖啡制作 ===
煮沸水
冲泡咖啡粉
倒入杯中
添加糖和牛奶
=== 茶制作 ===
煮沸水
浸泡茶叶
倒入杯中
添加柠檬
=== 带钩子的咖啡制作 ===
煮沸水
冲泡咖啡粉
倒入杯中
是否要添加调料?(y/n): y
添加糖和牛奶
=== 数据迁移 ===
执行数据备份...
验证MySQL数据完整性和约束...
从MySQL提取数据...
转换数据类型:MySQL类型 -> Oracle类型...
批量加载数据到Oracle...
清理临时资源...
=== 文件迁移 ===
文件迁移不需要备份,跳过
检查文件格式和编码.....
转换为BSON文档格式...
写入MongoDB...
清理临时资源...

框架中的应用案例

1 Spring Framework中的JdbcTemplate

public class JdbcTemplateExample {
    public class User {
        private Long id;
        private String name;
        private String email;
        // getters and setters...
    }
    public class UserRowMapper implements RowMapper<User> {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            user.setEmail(rs.getString("email"));
            return user;
        }
    }
    public void queryExample() {
        // JdbcTemplate定义查询模板
        String sql = "SELECT * FROM users WHERE id = ?";
        // 不需要管理连接、声明、结果集等资源
        User user = jdbcTemplate.queryForObject(
            sql, 
            new Object[]{1L}, 
            new UserRowMapper()
        );
    }
}

优缺点

优点:

  • ✅ 封装不变部分,扩展可变部分
  • ✅ 提取公共代码,便于维护
  • ✅ 行为由父类控制,子类实现具体细节

缺点:

  • ❌ 不同实现都需要定义一个子类,导致类数量增加
  • ❌ 父类修改算法骨架,子类无法感知

适用场景

  1. 多个类有相同的方法并且逻辑相同 - 抽取到抽象类
  2. 需要控制子类扩展 - 算法骨架固定
  3. 需要复用一个算法的不同变体 - 子类实现不同步骤

这个模式在实际开发中非常常见,特别是在框架设计中,它完美体现了"开闭原则"——对扩展开放,对修改关闭。

抱歉,评论功能暂时关闭!