文章目录
-
- 什么是模板模式?
- 核心思想
- 生活中的模板模式
- 模式结构
- 基础示例:饮料制作系统
-
- [1. 抽象模板类](#1. 抽象模板类)
- [2. 具体实现类](#2. 具体实现类)
- [3. 客户端使用](#3. 客户端使用)
- 完整示例:数据导出系统
-
- [1. 数据导出模板](#1. 数据导出模板)
- [2. 具体导出实现](#2. 具体导出实现)
- [3. 数据导出客户端](#3. 数据导出客户端)
- 实际应用示例:游戏角色系统
- 模板模式的优点
-
- [1. 代码复用](#1. 代码复用)
- [2. 提高扩展性](#2. 提高扩展性)
- [3. 便于维护](#3. 便于维护)
- 模板模式的缺点
-
- [1. 可能违反里氏替换原则](#1. 可能违反里氏替换原则)
- [2. 算法骨架固定](#2. 算法骨架固定)
- 适用场景
- 最佳实践
-
- [1. 合理使用final关键字](#1. 合理使用final关键字)
- [2. 使用钩子方法提供灵活性](#2. 使用钩子方法提供灵活性)
- [3. 合理设计抽象方法](#3. 合理设计抽象方法)
- [模板模式 vs 策略模式](#模板模式 vs 策略模式)
- 总结
什么是模板模式?
模板模式(Template Pattern)是一种行为型设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板模式使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。
核心思想
模板模式的核心思想是:定义一个算法的骨架,将一些步骤的实现延迟到子类中。模板模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
生活中的模板模式
想象一下泡茶和泡咖啡的过程:
- 烧水 → 冲泡 → 倒入杯子 → 添加调料
- 泡茶和泡咖啡的流程相同
- 但具体步骤的实现不同:
- 泡茶:用茶叶,加柠檬
- 泡咖啡:用咖啡粉,加糖和牛奶
这个固定流程就是模板!
模式结构
模板模式包含两个核心角色:
- 抽象类(Abstract Class):定义算法骨架和模板方法
- 具体类(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. 合理使用final关键字
java
public final void templateMethod() {
// 防止子类改变算法骨架
}
2. 使用钩子方法提供灵活性
java
protected void hook() {
// 子类可以选择性重写
}
3. 合理设计抽象方法
java
// 抽象方法应该是真正需要子类实现的
// 避免过度抽象
模板模式 vs 策略模式
| 模式 | 目的 | 控制权 |
|---|---|---|
| 模板模式 | 定义算法骨架 | 父类控制流程 |
| 策略模式 | 封装算法 | 客户端选择算法 |
总结
模板模式就像是"烹饪食谱",定义了做菜的步骤,但具体的调料和火候由厨师决定。
核心价值:
- 定义算法骨架,固定流程
- 代码复用,避免重复
- 便于维护和扩展
- 控制子类扩展
使用场景:
- 多个类有相同流程但不同实现时
- 需要固定算法步骤顺序时
- 提取公共代码进行重构时
简单记忆:
流程固定实现变?模板模式来解决!
骨架父类定,细节子类填。
掌握模板模式,能够让你在复杂系统中优雅地处理固定流程+可变实现的场景!