hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
2025本人正在沉淀中… 博客更新速度++
欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 1.1、什么是模板方法模式?
- 1.2、为什么要模板方法模式?
- 1.2.1、代码复用
- 1.2.2、符合开闭原则
- 1.2.3、支持框架设计
- 1.3、怎么实现模板方法模式?
- 二、模板方法模式在源码中运用
- 2.1、Java集合
- 2.2、AQS
- 三、总结
- 参考
模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个算法的框架,并允许子类在不改变算法结构的情况下重新定义算法的某些步骤。模板模式通过将算法的通用部分放在父类中,而将可变部分留给子类来实现。
模板模式的核心是将算法的通用部分放在父类中实现,而将可变的部分留给子类去实现。通过这种方式,模板模式实现了代码复用,同时提供了灵活性。
符合开闭原则,开闭原则(Open/Closed Principle)要求软件实体(类、模块、函数等)对扩展开放,对修改关闭。模板模式通过将可变的部分抽象出来,允许子类扩展算法的某些步骤,而无需修改父类的代码。 这符合开闭原则,提高了系统的可维护性和可扩展性。
示例:
在 Java 的 HttpServlet
中,service()
方法定义了处理 HTTP 请求的框架,但具体的 doGet()
和 doPost()
方法由子类实现。如果需要支持新的 HTTP 方法,只需添加新的方法,而无需修改 service()
方法。
模板模式在框架设计中非常有用。框架通常定义了一个通用的流程,但具体的实现由开发者完成。模板模式允许框架提供默认实现,同时允许开发者自定义某些步骤。
模板模式为框架设计提供了一种灵活且可扩展的方式。
示例:
在 Spring MVC 中,AbstractController
定义了处理 HTTP 请求的框架,但具体的请求处理逻辑由开发者实现。
定义一个抽象类: 写一个抽象类,里面包含一个模板方法(通常是 final 的),用来定义算法的步骤。在模板方法中,调用一些具体方法(有默认实现)和抽象方法(需要子类实现)。
实现具体类:创建一个子类,继承抽象类。实现抽象类中的抽象方法,提供具体的逻辑。
使用模板模式: 创建具体类的对象,调用模板方法。模板方法会自动按照定义好的步骤执行,同时调用子类实现的逻辑。
【案例】炒菜
炒菜的步骤是固定的,分为倒油、热油、倒菜、倒调料、翻炒等步骤。
AbstractClass类。这个abstract关键字只能在抽象类中,且子类必须实现abstract修饰的方法,就像2楼是盖在1楼上的。final关键字不是必须加的,看实际业务情况,加上后可以避免子类重写该方法。
public abstract class AbstractClass {
public final void cookProcess() {
//第一步:倒油
this.pourOil();
//第二步:热油
this.heatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
public void pourOil() {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
public void heatOil() {
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
public abstract void pourVegetable();
//第四步:倒调味料是不一样
public abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
public void fry(){
System.out.println("炒啊炒啊炒到熟啊");
}
}
ConcreteClass_BaoCai 类
public class ConcreteClass_BaoCai extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
ConcreteClass_CaiXin类
public class ConcreteClass_CaiXin extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
Clinet
public class Client {
public static void main(String[] args) {
//炒手撕包菜
ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
baoCai.cookProcess();
//炒蒜蓉菜心
ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
caiXin.cookProcess();
}
}
AbstractList
是抽象类,提供了列表和集合的基本能力。其中的通用addAll
方法,但是具体的实现add
方法的实现,交给了子类
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e); // 子类来实现
modified = true;
}
return modified;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
ArrayList的实现
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
LinkedList的实现
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
AQS 主要用于实现锁和同步器,它提供了抽象的框架方法,而具体的同步操作实现(如锁的获取、释放)则由子类来实现。这就是模板方法模式的关键特征:定义一个算法的骨架,将一些步骤延迟到子类中。
这里用tryAcquire
和tryRelease
举例
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
ReentrantLock 中 Sync类实现。ReentrantLock 是 AQS 的一个典型子类实现,它通过内部类 Sync 实现了tryAcquire
和tryRelease
方法。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); // 获取当前同步状态
if (c == 0) { // 如果状态为0,表示锁未被占用
if (!hasQueuedPredecessors() && // 检查是否有线程在等待
compareAndSetState(0, acquires)) { // CAS 设置状态
setExclusiveOwnerThread(current); // 设置当前线程为独占线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 如果是重入
int nextc = c + acquires;
if (nextc < 0) // 溢出检查
throw new Error("Maximum lock count exceeded");
setState(nextc); // 更新状态
return true;
}
return false; // 获取锁失败
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 计算新的状态
if (Thread.currentThread() != getExclusiveOwnerThread()) // 检查当前线程是否持有锁
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 如果状态为0,表示锁完全释放
free = true;
setExclusiveOwnerThread(null); // 清除独占线程
}
setState(c); // 更新状态
return free;
}
AQS 在acquire
和release
方法中会调用子类实现的tryAcquire
和tryRelease
。
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;
}
模板方法的结构:
● 抽象类(Abstract Class):定义一个算法的框架,包含一个模板方法和一些基本操作(可以是抽象的或已经实现的)。
● 模板方法(Template Method):在抽象类中定义的具体方法,它通过调用其他步骤(基本操作)来实现算法的步骤,子类可以通过覆盖某些步骤来调整具体的行为。
● 具体子类(Concrete Class):继承自抽象类,并实现或重写抽象类中的一些方法。
优点:
缺点:
适用场景:
重学 Java 设计模式:实战模板模式「模拟爬虫各类电商商品,生成营销推广海报场景」
黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili