文章目录
- [1. 生活中的例子](#1. 生活中的例子)
-
- [1.1 做菜的标准流程 🍳](#1.1 做菜的标准流程 🍳)
- [1.2 银行的办事流程 🏦](#1.2 银行的办事流程 🏦)
- [1.3 搭乐高玩具 🧱](#1.3 搭乐高玩具 🧱)
- [2. 代码中的例子](#2. 代码中的例子)
-
- [2.1 制作饮料的模板 ☕️](#2.1 制作饮料的模板 ☕️)
- [2.2 数据处理的模板 📊](#2.2 数据处理的模板 📊)
- [2.3 简单的游戏框架 🎮](#2.3 简单的游戏框架 🎮)
- [3. 归纳总结](#3. 归纳总结)
-
- [3.1 核心思想](#3.1 核心思想)
- [3.2 关键点总结表](#3.2 关键点总结表)
- [3.3 什么时候使用模板方法模式](#3.3 什么时候使用模板方法模式)
- [3.4 如果没有模板方法模式](#3.4 如果没有模板方法模式)
1. 生活中的例子
1.1 做菜的标准流程 🍳
想象你妈妈教你做菜:
- 妈妈告诉你:所有菜都要按这个顺序做:洗菜 → 切菜 → 开火 → 下锅 → 装盘
- 但西红柿炒蛋 和青椒肉丝 具体做法不同:
- 西红柿炒蛋:切西红柿 → 打鸡蛋 → 先炒蛋 → 再炒西红柿
- 青椒肉丝:切青椒 → 切肉丝 → 一起下锅炒
- 核心 :妈妈给了你固定流程(模板),具体每个步骤怎么实现,你自己决定
1.2 银行的办事流程 🏦
你去银行办理业务:
- 固定流程:取号 → 等待叫号 → 到窗口 → 办理业务 → 评价服务
- 但不同业务的具体内容不同:
- 存款:填存款单 → 交钱 → 拿回单
- 取款:填取款单 → 输密码 → 拿钱
- 核心 :银行规定了办事的标准流程,具体业务内容你自己处理
1.3 搭乐高玩具 🧱
每个乐高玩具的搭建说明:
- 固定模式:打开包装 → 分类零件 → 按图纸搭底座 → 搭主体 → 装饰
- 但不同的乐高:
- 城堡:先搭城墙 → 再搭塔楼
- 汽车:先搭底盘 → 再装轮子
- 核心 :乐高给了你搭建的框架,具体搭什么由图纸决定
2. 代码中的例子
2.1 制作饮料的模板 ☕️
java
// 抽象类:定义饮料制作的模板
abstract class BeverageMaker {
// 模板方法(固定流程,用final防止被子类修改)
public final void prepareBeverage() {
boilWater(); // 1.烧水(固定步骤)
brew(); // 2.冲泡(由子类实现)
pourInCup(); // 3.倒入杯子(固定步骤)
addCondiments(); // 4.加调料(由子类实现)
hook(); // 5.钩子方法(可选步骤)
}
// 固定步骤1
private void boilWater() {
System.out.println("正在烧水...");
}
// 固定步骤3
private void pourInCup() {
System.out.println("倒入杯子中");
}
// 抽象方法:必须由子类实现
protected abstract void brew(); // 冲泡方式
protected abstract void addCondiments(); // 添加调料
// 钩子方法:子类可以选择是否覆盖
protected void hook() {
// 默认什么都不做
}
}
// 具体实现1:泡茶
class TeaMaker extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用80℃水泡茶叶3分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入一片柠檬");
}
@Override
protected void hook() {
System.out.println("倒茶前先用热水温杯");
}
}
// 具体实现2:冲咖啡
class CoffeeMaker extends BeverageMaker {
@Override
protected void brew() {
System.out.println("用92℃水冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("加入牛奶和糖");
}
// 注意:这里没有覆盖hook()方法,所以使用默认实现(什么都不做)
}
// 使用示例
public class Main {
public static void main(String[] args) {
System.out.println("=== 泡茶 ===");
BeverageMaker tea = new TeaMaker();
tea.prepareBeverage();
System.out.println("\n=== 冲咖啡 ===");
BeverageMaker coffee = new CoffeeMaker();
coffee.prepareBeverage();
}
}
2.2 数据处理的模板 📊
java
// 抽象模板:数据处理流程
abstract class DataProcessor {
// 模板方法:固定数据处理流程
public final void processData() {
readData(); // 1.读取数据
transformData(); // 2.转换数据(由子类实现)
validateData(); // 3.验证数据(固定步骤)
saveData(); // 4.保存数据(由子类实现)
}
// 固定步骤
private void validateData() {
System.out.println("验证数据完整性...");
}
// 抽象方法
protected abstract void readData();
protected abstract void transformData();
protected abstract void saveData();
}
// 具体实现:从文件读取数据
class FileDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("从CSV文件读取数据");
}
@Override
protected void transformData() {
System.out.println("清洗和过滤数据");
}
@Override
protected void saveData() {
System.out.println("保存到数据库");
}
}
// 具体实现:从API读取数据
class APIDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("从REST API获取数据");
}
@Override
protected void transformData() {
System.out.println("将JSON转换为对象");
}
@Override
protected void saveData() {
System.out.println("保存到云存储");
}
}
2.3 简单的游戏框架 🎮
java
// 游戏模板
abstract class Game {
// 模板方法:游戏运行流程
public final void play() {
initialize(); // 1.初始化游戏
startPlay(); // 2.开始游戏(由子类实现)
endPlay(); // 3.结束游戏(由子类实现)
showScore(); // 4.显示分数(固定步骤)
}
// 固定步骤
private void initialize() {
System.out.println("初始化游戏资源...");
}
private void showScore() {
System.out.println("显示最终得分");
}
// 抽象方法
protected abstract void startPlay();
protected abstract void endPlay();
}
// 具体游戏1:象棋
class Chess extends Game {
@Override
protected void startPlay() {
System.out.println("象棋开始:红方先行");
}
@Override
protected void endPlay() {
System.out.println("象棋结束:将军!");
}
}
// 具体游戏2:足球游戏
class FootballGame extends Game {
@Override
protected void startPlay() {
System.out.println("足球比赛开始:开球!");
}
@Override
protected void endPlay() {
System.out.println("足球比赛结束:哨声响起");
}
}
3. 归纳总结
3.1 核心思想
"不要重复造轮子,但可以自定义零件"
3.2 关键点总结表
| 概念 | 比喻 | 代码对应 |
|---|---|---|
| 模板方法 | 做菜的固定流程 | prepareBeverage() |
| 抽象方法 | 必须自己做的步骤 | brew(), addCondiments() |
| 具体方法 | 已经帮你做好的步骤 | boilWater(), pourInCup() |
| 钩子方法 | 可选的额外步骤 | hook() |
3.3 什么时候使用模板方法模式
✅ 使用场景:
- 多个类有相同的流程 ,但某些步骤实现不同
- 需要控制子类的扩展,只允许修改特定步骤
- 想要避免代码重复,把公共代码提取到父类
❌ 不适合场景:
- 每个类的流程完全不同(没有共同模板)
- 步骤顺序经常变化(模板不稳定)
记住三句话:
- 模板方法是骨架:父类定义"做什么"和"顺序"
- 具体方法是血肉:子类定义"怎么做"的细节
- 钩子方法是装饰:可选的自定义扩展点
3.4 如果没有模板方法模式
请你想象一下:如果没有模板方法模式,泡茶和泡咖啡的代码会怎样?
- 你会写两个几乎一样的类
- 每次修改流程都要改两个地方
- 容易出错,维护困难
有了模板方法模式:
- 流程统一管理,修改一次全部生效
- 代码复用,避免重复
- 结构清晰,易于维护
这就是模板方法模式的魔力!🎯