设计模式之:模板模式

文章目录

什么是模板模式?

模板模式(Template Pattern)是一种行为型设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板模式使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。

核心思想

模板模式的核心思想是:定义一个算法的骨架,将一些步骤的实现延迟到子类中。模板模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

生活中的模板模式

想象一下泡茶和泡咖啡的过程:

  • 烧水 → 冲泡 → 倒入杯子 → 添加调料
  • 泡茶和泡咖啡的流程相同
  • 但具体步骤的实现不同:
    • 泡茶:用茶叶,加柠檬
    • 泡咖啡:用咖啡粉,加糖和牛奶

这个固定流程就是模板!

模式结构

模板模式包含两个核心角色:

  1. 抽象类(Abstract Class):定义算法骨架和模板方法
  2. 具体类(Concrete Class):实现算法中的特定步骤

基础示例:饮料制作系统

1. 抽象模板类

java 复制代码
/**
 * 饮料制作模板 - 抽象类
 * 定义饮料制作的算法骨架
 */
public abstract class BeverageMaker {
    
    /**
     * 模板方法 - 定义算法骨架(final防止子类重写)
     * 制作饮料的完整流程
     */
    public final void makeBeverage() {
        System.out.println("🥤 开始制作 " + getBeverageName());
        System.out.println("=" .repeat(40));
        
        boilWater();           // 步骤1:烧水
        brew();                // 步骤2:冲泡
        pourInCup();           // 步骤3:倒入杯子
        addCondiments();       // 步骤4:添加调料
        hook();                // 钩子方法(可选)
        
        System.out.println("✅ " + getBeverageName() + " 制作完成!");
        System.out.println();
    }
    
    /**
     * 烧水 - 具体步骤(通用实现)
     */
    private void boilWater() {
        System.out.println("1. 💧 烧开水");
        // 模拟烧水过程
        try {
            Thread.sleep(500);
            System.out.println("   ✅ 水烧开了");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 冲泡 - 抽象方法(由子类实现)
     */
    protected abstract void brew();
    
    /**
     * 倒入杯子 - 具体步骤(通用实现)
     */
    private void pourInCup() {
        System.out.println("3. 🥤 倒入杯子");
    }
    
    /**
     * 添加调料 - 抽象方法(由子类实现)
     */
    protected abstract void addCondiments();
    
    /**
     * 获取饮料名称 - 抽象方法
     */
    protected abstract String getBeverageName();
    
    /**
     * 钩子方法 - 可选步骤(子类可以重写)
     * 默认什么都不做
     */
    protected void hook() {
        // 空实现,子类可以选择性重写
    }
    
    /**
     * 是否需要调料 - 钩子方法
     * 子类可以重写以改变算法流程
     */
    protected boolean customerWantsCondiments() {
        return true; // 默认需要调料
    }
}

2. 具体实现类

java 复制代码
/**
 * 茶制作类 - 具体类
 */
public class TeaMaker extends BeverageMaker {
    
    @Override
    protected void brew() {
        System.out.println("2. 🍃 用沸水浸泡茶叶");
        // 模拟泡茶过程
        try {
            Thread.sleep(1000);
            System.out.println("   ✅ 茶叶浸泡完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Override
    protected void addCondiments() {
        if (customerWantsCondiments()) {
            System.out.println("4. 🍋 添加柠檬");
        } else {
            System.out.println("4. ⏭️  跳过添加调料(顾客要求)");
        }
    }
    
    @Override
    protected String getBeverageName() {
        return "茶";
    }
    
    @Override
    protected void hook() {
        System.out.println("5. 🌿 加入一片薄荷叶装饰");
    }
}

/**
 * 咖啡制作类 - 具体类
 */
public class CoffeeMaker extends BeverageMaker {
    private boolean withSugar = true;
    private boolean withMilk = true;
    
    public CoffeeMaker() {}
    
    public CoffeeMaker(boolean withSugar, boolean withMilk) {
        this.withSugar = withSugar;
        this.withMilk = withMilk;
    }
    
    @Override
    protected void brew() {
        System.out.println("2. ☕ 用沸水冲泡咖啡粉");
        // 模拟冲泡过程
        try {
            Thread.sleep(800);
            System.out.println("   ✅ 咖啡冲泡完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Override
    protected void addCondiments() {
        if (!customerWantsCondiments()) {
            System.out.println("4. ⏭️  跳过添加调料(黑咖啡)");
            return;
        }
        
        if (withSugar) {
            System.out.println("4. 🍬 加糖");
        }
        if (withMilk) {
            System.out.println("   🥛 加牛奶");
        }
        
        if (!withSugar && !withMilk) {
            System.out.println("4. ⚫ 纯黑咖啡,不添加任何调料");
        }
    }
    
    @Override
    protected String getBeverageName() {
        String name = "咖啡";
        if (!withSugar && !withMilk) {
            name = "黑" + name;
        } else if (withMilk && !withSugar) {
            name = "拿铁" + name;
        }
        return name;
    }
    
    @Override
    protected boolean customerWantsCondiments() {
        // 如果既不要糖也不要牛奶,就返回false
        return withSugar || withMilk;
    }
}

/**
 * 热巧克力制作类 - 具体类
 */
public class HotChocolateMaker extends BeverageMaker {
    
    @Override
    protected void brew() {
        System.out.println("2. 🍫 融化巧克力粉");
        // 模拟融化过程
        try {
            Thread.sleep(700);
            System.out.println("   ✅ 巧克力融化完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Override
    protected void addCondiments() {
        System.out.println("4. 🍭 加入棉花糖");
        System.out.println("   🍫 撒上巧克力碎");
    }
    
    @Override
    protected String getBeverageName() {
        return "热巧克力";
    }
    
    @Override
    protected void hook() {
        System.out.println("5. 🔥 用喷枪轻微烤焦棉花糖");
    }
}

3. 客户端使用

java 复制代码
/**
 * 咖啡馆客户端
 */
public class CoffeeShop {
    public static void main(String[] args) {
        System.out.println("=== 模板模式演示 - 咖啡馆系统 ===\n");
        
        // 演示1:基础饮料制作
        demonstrateBasicBeverages();
        
        // 演示2:定制化饮料
        demonstrateCustomizedBeverages();
        
        // 演示3:钩子方法使用
        demonstrateHookMethods();
    }
    
    /**
     * 演示基础饮料制作
     */
    private static void demonstrateBasicBeverages() {
        System.out.println("1. 基础饮料制作演示:");
        System.out.println("=" .repeat(50));
        
        // 制作茶
        BeverageMaker teaMaker = new TeaMaker();
        teaMaker.makeBeverage();
        
        // 制作咖啡
        BeverageMaker coffeeMaker = new CoffeeMaker();
        coffeeMaker.makeBeverage();
        
        // 制作热巧克力
        BeverageMaker chocolateMaker = new HotChocolateMaker();
        chocolateMaker.makeBeverage();
    }
    
    /**
     * 演示定制化饮料
     */
    private static void demonstrateCustomizedBeverages() {
        System.out.println("2. 定制化饮料演示:");
        System.out.println("=" .repeat(50));
        
        // 制作黑咖啡(无糖无奶)
        System.out.println("顾客要求:黑咖啡(无糖无奶)");
        BeverageMaker blackCoffee = new CoffeeMaker(false, false);
        blackCoffee.makeBeverage();
        
        // 制作拿铁咖啡(无糖有奶)
        System.out.println("顾客要求:拿铁咖啡(无糖有奶)");
        BeverageMaker latte = new CoffeeMaker(false, true);
        latte.makeBeverage();
    }
    
    /**
     * 演示钩子方法
     */
    private static void demonstrateHookMethods() {
        System.out.println("3. 钩子方法演示:");
        System.out.println("=" .repeat(50));
        
        // 创建特殊茶类,重写钩子方法
        BeverageMaker specialTea = new TeaMaker() {
            @Override
            protected boolean customerWantsCondiments() {
                // 顾客不想要柠檬
                return false;
            }
            
            @Override
            protected void hook() {
                System.out.println("5. 🌸 加入干玫瑰花装饰");
            }
        };
        
        specialTea.makeBeverage();
    }
}

完整示例:数据导出系统

让我们通过一个更复杂的数据导出系统来深入理解模板模式。

1. 数据导出模板

java 复制代码
import java.util.List;

/**
 * 数据导出模板 - 抽象类
 * 定义数据导出的完整流程
 */
public abstract class DataExporter {
    
    /**
     * 模板方法 - 导出数据的完整流程(final)
     */
    public final void exportData() {
        System.out.println("📊 开始数据导出流程");
        System.out.println("=" .repeat(50));
        
        // 1. 验证数据
        if (!validateData()) {
            System.out.println("❌ 数据验证失败,导出中止");
            return;
        }
        
        // 2. 准备数据
        List<?> data = prepareData();
        if (data == null || data.isEmpty()) {
            System.out.println("⚠️ 无数据可导出");
            return;
        }
        
        // 3. 转换数据格式
        String formattedData = formatData(data);
        
        // 4. 生成文件
        String filePath = generateFile(formattedData);
        
        // 5. 后处理
        postProcess(filePath);
        
        // 6. 清理资源
        cleanup();
        
        System.out.println("✅ 数据导出完成: " + filePath);
        System.out.println();
    }
    
    /**
     * 验证数据 - 具体步骤
     */
    private boolean validateData() {
        System.out.println("1. 🔍 验证数据...");
        boolean isValid = performValidation();
        System.out.println("   验证结果: " + (isValid ? "✅ 通过" : "❌ 失败"));
        return isValid;
    }
    
    /**
     * 执行具体验证 - 抽象方法
     */
    protected abstract boolean performValidation();
    
    /**
     * 准备数据 - 具体步骤
     */
    private List<?> prepareData() {
        System.out.println("2. 📋 准备数据...");
        List<?> data = fetchData();
        if (data != null) {
            System.out.println("   获取到 " + data.size() + " 条数据");
        }
        return data;
    }
    
    /**
     * 获取数据 - 抽象方法
     */
    protected abstract List<?> fetchData();
    
    /**
     * 转换数据格式 - 具体步骤
     */
    private String formatData(List<?> data) {
        System.out.println("3. 🔄 转换数据格式...");
        String formatted = doFormat(data);
        System.out.println("   格式转换完成");
        return formatted;
    }
    
    /**
     * 执行格式转换 - 抽象方法
     */
    protected abstract String doFormat(List<?> data);
    
    /**
     * 生成文件 - 具体步骤
     */
    private String generateFile(String data) {
        System.out.println("4. 💾 生成文件...");
        String filePath = createFile(data);
        System.out.println("   文件生成: " + filePath);
        return filePath;
    }
    
    /**
     * 创建文件 - 抽象方法
     */
    protected abstract String createFile(String data);
    
    /**
     * 后处理 - 具体步骤
     */
    private void postProcess(String filePath) {
        System.out.println("5. 🚀 执行后处理...");
        doPostProcess(filePath);
    }
    
    /**
     * 执行后处理 - 钩子方法(可选)
     */
    protected void doPostProcess(String filePath) {
        // 默认空实现
    }
    
    /**
     * 清理资源 - 具体步骤
     */
    private void cleanup() {
        System.out.println("6. 🧹 清理资源...");
        doCleanup();
        System.out.println("   资源清理完成");
    }
    
    /**
     * 执行清理 - 钩子方法(可选)
     */
    protected void doCleanup() {
        // 默认空实现
    }
    
    /**
     * 获取导出类型名称
     */
    protected abstract String getExportType();
}

2. 具体导出实现

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * CSV数据导出 - 具体类
 */
public class CsvDataExporter extends DataExporter {
    private final String dataSource;
    
    public CsvDataExporter(String dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    protected boolean performValidation() {
        // 模拟CSV数据验证
        System.out.println("   📝 检查数据源连接: " + dataSource);
        System.out.println("   📝 验证数据完整性");
        return dataSource != null && !dataSource.isEmpty();
    }
    
    @Override
    protected List<String> fetchData() {
        // 模拟从数据源获取数据
        System.out.println("   📥 从 " + dataSource + " 获取数据");
        
        List<String> data = new ArrayList<>();
        data.add("姓名,年龄,城市");
        data.add("张三,25,北京");
        data.add("李四,30,上海");
        data.add("王五,28,广州");
        
        // 模拟数据获取延迟
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return data;
    }
    
    @Override
    protected String doFormat(List<?> data) {
        // 将数据格式化为CSV
        StringBuilder csv = new StringBuilder();
        for (Object row : data) {
            csv.append(row).append("\n");
        }
        return csv.toString();
    }
    
    @Override
    protected String createFile(String data) {
        String fileName = "export_" + System.currentTimeMillis() + ".csv";
        // 模拟文件创建
        System.out.println("   💽 创建CSV文件: " + fileName);
        System.out.println("   📄 写入数据: " + data.split("\n").length + " 行");
        return "/exports/" + fileName;
    }
    
    @Override
    protected void doPostProcess(String filePath) {
        // CSV导出后处理:生成统计信息
        System.out.println("   📈 生成数据统计报告");
    }
    
    @Override
    protected String getExportType() {
        return "CSV";
    }
}

/**
 * JSON数据导出 - 具体类
 */
public class JsonDataExporter extends DataExporter {
    private final String apiEndpoint;
    
    public JsonDataExporter(String apiEndpoint) {
        this.apiEndpoint = apiEndpoint;
    }
    
    @Override
    protected boolean performValidation() {
        // 模拟JSON API验证
        System.out.println("   🌐 验证API端点: " + apiEndpoint);
        System.out.println("   🔑 检查API密钥");
        return apiEndpoint != null && apiEndpoint.startsWith("https://");
    }
    
    @Override
    protected List<User> fetchData() {
        // 模拟从API获取数据
        System.out.println("   📡 调用API: " + apiEndpoint);
        
        List<User> users = new ArrayList<>();
        users.add(new User("张三", 25, "北京"));
        users.add(new User("李四", 30, "上海"));
        users.add(new User("王五", 28, "广州"));
        
        // 模拟网络延迟
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return users;
    }
    
    @Override
    protected String doFormat(List<?> data) {
        // 将数据格式化为JSON
        StringBuilder json = new StringBuilder();
        json.append("{\n");
        json.append("  \"users\": [\n");
        
        for (int i = 0; i < data.size(); i++) {
            User user = (User) data.get(i);
            json.append("    {\n");
            json.append("      \"name\": \"").append(user.getName()).append("\",\n");
            json.append("      \"age\": ").append(user.getAge()).append(",\n");
            json.append("      \"city\": \"").append(user.getCity()).append("\"\n");
            json.append("    }");
            if (i < data.size() - 1) {
                json.append(",");
            }
            json.append("\n");
        }
        
        json.append("  ]\n");
        json.append("}");
        return json.toString();
    }
    
    @Override
    protected String createFile(String data) {
        String fileName = "export_" + System.currentTimeMillis() + ".json";
        // 模拟文件创建
        System.out.println("   💽 创建JSON文件: " + fileName);
        System.out.println("   📄 写入数据: " + data.split("\n").length + " 行");
        return "/exports/" + fileName;
    }
    
    @Override
    protected void doPostProcess(String filePath) {
        // JSON导出后处理:验证JSON格式
        System.out.println("   ✅ 验证JSON格式完整性");
        System.out.println("   🔍 检查数据编码");
    }
    
    @Override
    protected void doCleanup() {
        // JSON导出特殊清理:关闭网络连接
        System.out.println("   🔌 关闭API连接");
    }
    
    @Override
    protected String getExportType() {
        return "JSON";
    }
}

/**
 * Excel数据导出 - 具体类
 */
public class ExcelDataExporter extends DataExporter {
    private final String databaseTable;
    
    public ExcelDataExporter(String databaseTable) {
        this.databaseTable = databaseTable;
    }
    
    @Override
    protected boolean performValidation() {
        // 模拟数据库验证
        System.out.println("   🗄️  检查数据库表: " + databaseTable);
        System.out.println("   📊 验证表结构");
        return databaseTable != null && databaseTable.length() > 0;
    }
    
    @Override
    protected List<Product> fetchData() {
        // 模拟从数据库获取数据
        System.out.println("   🗃️  查询数据库表: " + databaseTable);
        
        List<Product> products = new ArrayList<>();
        products.add(new Product("笔记本电脑", 5999.99, 50));
        products.add(new Product("智能手机", 3999.00, 100));
        products.add(new Product("平板电脑", 2999.50, 30));
        
        // 模拟数据库查询延迟
        try {
            Thread.sleep(1200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        return products;
    }
    
    @Override
    protected String doFormat(List<?> data) {
        // 模拟Excel格式转换
        StringBuilder excelData = new StringBuilder();
        excelData.append("产品名称\t价格\t库存\n");
        
        for (Object item : data) {
            Product product = (Product) item;
            excelData.append(product.getName()).append("\t")
                    .append(product.getPrice()).append("\t")
                    .append(product.getStock()).append("\n");
        }
        
        return excelData.toString();
    }
    
    @Override
    protected String createFile(String data) {
        String fileName = "export_" + System.currentTimeMillis() + ".xlsx";
        // 模拟Excel文件创建
        System.out.println("   💽 创建Excel文件: " + fileName);
        System.out.println("   📊 创建工作表和数据透视表");
        return "/exports/" + fileName;
    }
    
    @Override
    protected void doPostProcess(String filePath) {
        // Excel导出后处理
        System.out.println("   📊 生成图表和数据分析");
        System.out.println("   🔢 计算汇总统计");
    }
    
    @Override
    protected void doCleanup() {
        // Excel导出特殊清理
        System.out.println("   🗃️  关闭数据库连接");
        System.out.println("   🧮 清理临时缓存");
    }
    
    @Override
    protected String getExportType() {
        return "Excel";
    }
}

/**
 * 用户数据类
 */
class User {
    private String name;
    private int age;
    private String city;
    
    public User(String name, int age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }
    
    // Getter方法
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getCity() { return city; }
}

/**
 * 产品数据类
 */
class Product {
    private String name;
    private double price;
    private int stock;
    
    public Product(String name, double price, int stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }
    
    // Getter方法
    public String getName() { return name; }
    public double getPrice() { return price; }
    public int getStock() { return stock; }
}

3. 数据导出客户端

java 复制代码
/**
 * 数据导出系统客户端
 */
public class DataExportSystem {
    public static void main(String[] args) {
        System.out.println("=== 模板模式演示 - 数据导出系统 ===\n");
        
        // 演示1:不同格式的数据导出
        demonstrateDifferentFormats();
        
        // 演示2:导出流程分析
        demonstrateExportProcess();
        
        // 演示3:错误处理演示
        demonstrateErrorHandling();
    }
    
    /**
     * 演示不同格式的数据导出
     */
    private static void demonstrateDifferentFormats() {
        System.out.println("1. 不同格式数据导出演示:");
        System.out.println("=" .repeat(50));
        
        // CSV导出
        DataExporter csvExporter = new CsvDataExporter("user_database");
        csvExporter.exportData();
        
        // JSON导出
        DataExporter jsonExporter = new JsonDataExporter("https://api.example.com/users");
        jsonExporter.exportData();
        
        // Excel导出
        DataExporter excelExporter = new ExcelDataExporter("products");
        excelExporter.exportData();
    }
    
    /**
     * 演示导出流程分析
     */
    private static void demonstrateExportProcess() {
        System.out.println("2. 导出流程分析演示:");
        System.out.println("=" .repeat(50));
        
        // 创建性能监控的导出器
        DataExporter monitoredExporter = new CsvDataExporter("performance_test") {
            private long startTime;
            
            @Override
            protected List<String> fetchData() {
                startTime = System.currentTimeMillis();
                return super.fetchData();
            }
            
            @Override
            protected void doPostProcess(String filePath) {
                super.doPostProcess(filePath);
                long endTime = System.currentTimeMillis();
                System.out.println("   ⏱️  数据获取耗时: " + (endTime - startTime) + "ms");
            }
        };
        
        monitoredExporter.exportData();
    }
    
    /**
     * 演示错误处理
     */
    private static void demonstrateErrorHandling() {
        System.out.println("3. 错误处理演示:");
        System.out.println("=" .repeat(50));
        
        // 创建会失败的导出器
        DataExporter failingExporter = new JsonDataExporter("invalid-endpoint") {
            @Override
            protected boolean performValidation() {
                System.out.println("   ❌ 模拟验证失败");
                return false; // 故意返回false
            }
        };
        
        failingExporter.exportData();
        
        // 创建空数据导出器
        DataExporter emptyExporter = new CsvDataExporter("empty_source") {
            @Override
            protected List<String> fetchData() {
                System.out.println("   📭 数据源为空");
                return new ArrayList<>(); // 返回空列表
            }
        };
        
        emptyExporter.exportData();
    }
}

实际应用示例:游戏角色系统

java 复制代码
/**
 * 游戏角色系统 - 模板模式实际应用
 */
public class GameCharacterSystem {
    public static void main(String[] args) {
        System.out.println("=== 模板模式演示 - 游戏角色系统 ===\n");
        
        // 创建不同职业的角色
        Character warrior = new Warrior("雷霆战神");
        Character mage = new Mage("火焰法师");
        Character archer = new Archer("神射手");
        
        // 执行角色行动
        warrior.performAction();
        mage.performAction();
        archer.performAction();
    }
}

/**
 * 游戏角色模板 - 抽象类
 */
abstract class Character {
    protected String name;
    protected int health;
    protected int mana;
    
    public Character(String name) {
        this.name = name;
        this.health = 100;
        this.mana = 100;
    }
    
    /**
     * 模板方法 - 角色行动流程
     */
    public final void performAction() {
        System.out.println("\n🎮 " + name + " 开始行动");
        System.out.println("-".repeat(30));
        
        // 1. 检查状态
        if (!checkStatus()) {
            System.out.println("❌ 状态不佳,无法行动");
            return;
        }
        
        // 2. 选择技能
        String skill = selectSkill();
        
        // 3. 执行攻击
        attack(skill);
        
        // 4. 消耗资源
        consumeResources();
        
        // 5. 显示状态
        displayStatus();
        
        // 6. 特殊效果(钩子方法)
        specialEffect();
    }
    
    /**
     * 检查状态 - 具体步骤
     */
    private boolean checkStatus() {
        System.out.println("1. ❤️  检查生命值: " + health);
        System.out.println("   🔮 检查魔法值: " + mana);
        return health > 0 && mana > 0;
    }
    
    /**
     * 选择技能 - 抽象方法
     */
    protected abstract String selectSkill();
    
    /**
     * 执行攻击 - 抽象方法
     */
    protected abstract void attack(String skill);
    
    /**
     * 消耗资源 - 具体步骤
     */
    private void consumeResources() {
        int manaCost = calculateManaCost();
        mana -= manaCost;
        System.out.println("4. ⚡ 消耗魔法值: " + manaCost);
    }
    
    /**
     * 计算魔法消耗 - 抽象方法
     */
    protected abstract int calculateManaCost();
    
    /**
     * 显示状态 - 具体步骤
     */
    private void displayStatus() {
        System.out.println("5. 📊 当前状态:");
        System.out.println("   生命值: " + health + " ❤️");
        System.out.println("   魔法值: " + mana + " 🔮");
    }
    
    /**
     * 特殊效果 - 钩子方法
     */
    protected void specialEffect() {
        // 默认无特殊效果
    }
    
    public String getName() {
        return name;
    }
}

/**
 * 战士 - 具体类
 */
class Warrior extends Character {
    public Warrior(String name) {
        super(name);
        this.health = 150; // 战士生命值更高
    }
    
    @Override
    protected String selectSkill() {
        String skill = "旋风斩";
        System.out.println("2. ⚔️  选择技能: " + skill);
        return skill;
    }
    
    @Override
    protected void attack(String skill) {
        System.out.println("3. 💥 执行攻击: " + skill);
        System.out.println("   🎯 对周围敌人造成范围伤害");
    }
    
    @Override
    protected int calculateManaCost() {
        return 20;
    }
    
    @Override
    protected void specialEffect() {
        System.out.println("6. 🛡️  触发格挡效果,减少受到的伤害");
    }
}

/**
 * 法师 - 具体类
 */
class Mage extends Character {
    public Mage(String name) {
        super(name);
        this.mana = 150; // 法师魔法值更高
    }
    
    @Override
    protected String selectSkill() {
        String skill = "火球术";
        System.out.println("2. 🔥 选择技能: " + skill);
        return skill;
    }
    
    @Override
    protected void attack(String skill) {
        System.out.println("3. 🔥 执行攻击: " + skill);
        System.out.println("   💫 发射火球,造成火焰伤害");
    }
    
    @Override
    protected int calculateManaCost() {
        return 30;
    }
    
    @Override
    protected void specialEffect() {
        System.out.println("6. ✨ 触发法术连击,下一个法术消耗减少");
    }
}

/**
 * 弓箭手 - 具体类
 */
class Archer extends Character {
    public Archer(String name) {
        super(name);
    }
    
    @Override
    protected String selectSkill() {
        String skill = "多重射击";
        System.out.println("2. 🏹 选择技能: " + skill);
        return skill;
    }
    
    @Override
    protected void attack(String skill) {
        System.out.println("3. 🎯 执行攻击: " + skill);
        System.out.println("   📍 同时攻击多个目标");
    }
    
    @Override
    protected int calculateManaCost() {
        return 15;
    }
    
    @Override
    protected void specialEffect() {
        System.out.println("6. 🎪 触发敏捷效果,移动速度提升");
    }
}

模板模式的优点

1. 代码复用

java 复制代码
// 将公共代码放在抽象类中
// 避免在多个子类中重复相同代码

2. 提高扩展性

java 复制代码
// 新增具体类很容易
// 只需要实现抽象方法即可

3. 便于维护

java 复制代码
// 算法骨架集中在一个地方
// 修改算法流程时只需要修改模板类

模板模式的缺点

1. 可能违反里氏替换原则

java 复制代码
// 如果子类对模板方法进行重写
// 可能破坏算法的完整性

2. 算法骨架固定

java 复制代码
// 算法的步骤顺序是固定的
// 不够灵活

适用场景

  1. 多个类有相同的方法,但具体实现不同时
  2. 需要控制子类扩展时
  3. 重要、复杂的算法,需要固定算法骨架时
  4. 重构时提取公共代码时

最佳实践

1. 合理使用final关键字

java 复制代码
public final void templateMethod() {
    // 防止子类改变算法骨架
}

2. 使用钩子方法提供灵活性

java 复制代码
protected void hook() {
    // 子类可以选择性重写
}

3. 合理设计抽象方法

java 复制代码
// 抽象方法应该是真正需要子类实现的
// 避免过度抽象

模板模式 vs 策略模式

模式 目的 控制权
模板模式 定义算法骨架 父类控制流程
策略模式 封装算法 客户端选择算法

总结

模板模式就像是"烹饪食谱",定义了做菜的步骤,但具体的调料和火候由厨师决定。

核心价值:

  • 定义算法骨架,固定流程
  • 代码复用,避免重复
  • 便于维护和扩展
  • 控制子类扩展

使用场景:

  • 多个类有相同流程但不同实现时
  • 需要固定算法步骤顺序时
  • 提取公共代码进行重构时

简单记忆:

流程固定实现变?模板模式来解决!

骨架父类定,细节子类填。

掌握模板模式,能够让你在复杂系统中优雅地处理固定流程+可变实现的场景!

相关推荐
开发者小天3 小时前
调整为 dart-sass 支持的语法,将深度选择器/deep/调整为::v-deep
开发语言·前端·javascript·vue.js·uni-app·sass·1024程序员节
lapiii3583 小时前
14天极限复习软考day4-法律、设计模式
设计模式
老猿讲编程4 小时前
C++中的奇异递归模板模式CRTP
开发语言·c++
紫荆鱼6 小时前
设计模式-迭代器模式(Iterator)
c++·后端·设计模式·迭代器模式
汤姆yu6 小时前
基于python的化妆品销售分析系统
开发语言·python·化妆品销售分析
ScilogyHunter6 小时前
C语言标准库完全指南
c语言·开发语言
sali-tec6 小时前
C# 基于halcon的视觉工作流-章52-生成标定板
开发语言·图像处理·人工智能·算法·计算机视觉
应茶茶6 小时前
C++11 核心新特性:从语法重构到工程化实践
java·开发语言·c++
程子的小段7 小时前
C 语言实例 - 字符串复制
c语言·开发语言