高 基础
模板方法模式#
一句话答案#
父类定义算法骨架(abstract 方法),子类实现具体步骤,不改变算法结构;JdbcTemplate/RestTemplate 是典型。
核心要点
模板方法模式在父类中定义算法的骨架(执行流程),将某些步骤的实现延迟到子类。子类可以重新定义算法中的某些步骤,但不能改变算法的整体结构。
核心结构:
public abstract class AbstractTemplate {
// 模板方法:定义算法骨架,用 final 防止子类覆盖
public final void templateMethod() {
step1(); // 固定步骤(父类实现)
step2(); // 抽象步骤(子类实现)
if (hook()) { // 钩子方法(子类可选覆盖)
step3();
}
step4(); // 固定步骤
}
private void step1() { /* 固定实现 */ }
protected abstract void step2(); // 子类必须实现
protected boolean hook() { return true; } // 钩子:默认返回 true,子类可覆盖
protected abstract void step3();
private void step4() { /* 固定实现 */ }
}javaAQS 中的模板方法:
AbstractQueuedSynchronizer(AQS)是 Java 并发包的基础框架,典型的模板方法模式。→ 详见 AQS原理
AQS 定义了获取/释放锁的模板方法(不可重写),子类只需实现尝试获取/释放的方法:
// AQS 的模板方法(final,不可重写)
public final void acquire(int arg) {
if (!tryAcquire(arg) && // ← 子类实现
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
public final boolean release(int arg) {
if (tryRelease(arg)) { // ← 子类实现
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 子类需要实现的方法(钩子)
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
protected boolean tryRelease(int arg) { throw new UnsupportedOperationException(); }java| AQS 子类 | 实现的方法 | 用途 |
|---|---|---|
ReentrantLock.Sync | tryAcquire / tryRelease | 可重入独占锁 |
Semaphore.Sync | tryAcquireShared / tryReleaseShared | 信号量 |
CountDownLatch.Sync | tryAcquireShared / tryReleaseShared | 倒计时门栓 |
Spring 中的模板方法:
JdbcTemplate:获取连接 → 执行 SQL(回调) → 释放资源AbstractApplicationContext.refresh():Spring 容器启动的 12 步固定流程RestTemplate:构建请求 → 执行请求 → 处理响应
模板方法 vs 策略模式:
| 维度 | 模板方法 | 策略模式 |
|---|---|---|
| 实现方式 | 继承(子类重写抽象方法) | 组合(传入不同策略对象) |
| 算法完整性 | 父类定义骨架,子类实现部分步骤 | 策略类实现完整算法 |
| 灵活性 | 编译期确定 | 运行时可动态切换 |
面试回答(2分钟版)
模板方法模式的核心思想是在父类中用final方法定义算法骨架,把其中可变的步骤声明为abstract方法交给子类实现。这样算法的整体流程不变,但具体步骤可以定制。Java并发包中的AQS就是经典的模板方法:acquire()和release()是final的模板方法定义了获取释放锁的完整流程,而tryAcquire()和tryRelease()是抽象方法由子类实现,ReentrantLock实现独占锁逻辑,Semaphore实现共享信号量逻辑,CountDownLatch实现倒计时门栓逻辑。Spring中也大量使用:JdbcTemplate封装了获取连接、执行SQL、释放资源的骨架,我们只需传入SQL和回调处理结果;AbstractApplicationContext的refresh()定义了容器启动的12步固定流程。模板方法还有一个钩子方法的概念,子类可以选择性覆盖来控制流程中的某些分支。和策略模式的区别在于模板方法通过继承在编译期确定,策略模式通过组合可以运行时动态切换。
追问与易错
追问方向:
- “钩子方法是什么?”→ 父类中提供默认实现的方法,子类可以选择性覆盖来影响模板方法的执行流程(如控制某个步骤是否执行),是模板方法模式的扩展点
- “和策略模式区别?”→ 模板方法通过继承在编译期确定,父类定义骨架子类实现部分步骤;策略模式通过组合在运行时动态切换,策略类实现完整算法且相互独立
- “Spring 哪里用了模板方法?”→ JdbcTemplate(获取连接→执行 SQL→释放资源)、AbstractApplicationContext.refresh()(容器启动 12 步固定流程)、RestTemplate(构建请求→执行→处理响应)
易错点:
- ❌ 模板方法灵活性差——钩子方法提供扩展点
- ❌ 混淆模板方法和模板引擎