设计模式 14 模板模式
模板模式(Template Pattern)属于行为型模式
概述
在生活中常常会遇到这样的情况,做某一件事情,有些步骤是固定的,有些步骤的变化的。
比如去医院看病,挂号和排队这两个步骤是固定的,不管是什么病到了医院都得遵循这两个步骤;但是后续的检查和治疗这两个步骤是变化的,不同的病需要采用不同的检查方式,然后采取不同的治疗手段。
针对这一情况,在设计去医院看病这一程序时可以这样实现:定义一个抽象类,固定的步骤用普通方法实现,变化的步骤定义为抽象方法,由子类继承实现。这样就可以根据不同的子类来实现不同的变化步骤,这就是模板模式。
代码实现
这里以去医院看病来介绍模板模式:
1、定义抽象疾病
/**
* 疾病
*/
public abstract class Disease {
/**
* 治病
*/
public void cure() {
// 1、挂号
registered();
// 2、排队
queue();
// 3、检查
check();
// 4、治疗
treat();
}
/**
* 挂号
*/
public void registered() {
System.out.println("1、挂号");
}
/**
* 排队
*/
public void queue() {
System.out.println("2、排队");
}
/**
* 检查
*/
public abstract void check();
/**
* 治疗
*/
public abstract void treat();
}
2、定义具体疾病
感冒:
/**
* 感冒
*/
public class Cold extends Disease {
@Override
public void check() {
System.out.println("3、感冒检查:量体温");
}
@Override
public void treat() {
System.out.println("4、感冒开药:感冒灵");
}
}
口腔溃疡:
/**
* 口腔溃疡
*/
public class MouthUlcers extends Disease {
@Override
public void check() {
System.out.println("3、口腔溃疡检查:看口腔");
}
@Override
public void treat() {
System.out.println("4、口腔溃疡开药:西瓜霜");
}
}
3、调用
// 感冒
Disease cold = new Cold();
// 口腔溃疡
Disease mouthUlcers = new MouthUlcers();
// 治疗感冒
cold.cure();
// 治疗口腔溃疡
mouthUlcers.cure();
输出结果:
1、挂号
2、排队
3、感冒检查:量体温
4、感冒开药:感冒灵
1、挂号
2、排队
3、口腔溃疡检查:看口腔
4、口腔溃疡开药:西瓜霜
可以发现,在治疗感冒和口腔溃疡这两种疾病时,前两个步骤是一样的,只有后两个步骤是不同的,这样就实现了复用相同的步骤,后续再定义其他疾病时只需要继承 Disease 实现其抽象方法即可,复用性和扩展性都大大提高。
优缺点
优点
1、利用模板方法将相同处理逻辑的代码放到抽象父类中,提高了代码的复用性。
2、将不同的代码交由不同的子类实现,通过对子类的扩展增加新的行为,提高代码的扩展性。
缺点
1、每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
2、继承关系自身的缺点,如果父类添加新的抽象方法,所有子类都要添加该方法。
使用场景
1、有多个子类共有的方法,且逻辑相同。
2、重要的、复杂的方法,可以考虑作为模板方法。
JUC 中的 AQS 判断是否发布方法
tryRelease
的实现
AbstractQueuedSynchronizer
/**
* 锁释放操作
*/
public final boolean release(int arg) {
// 调用 tryRelease 方法判断是否释放锁
// 但是此方法并不是在 AQS 实现的,而是不同的锁自行实现
// 因为 AQS 也不知道你这种类型的锁到底该怎么去解锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
ReentrantLock.Sync
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
}
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
AbstractQueuedSynchronizer 中没有实现 tryRelease
,而是在其子类 ReentrantLock.Sync 中实现。这就是对模板模式的典型应用。
注意事项
为防止恶意操作,一般模板方法都会加上 final
关键词。
参考
https://www.bilibili.com/video/BV1u3411P7Na?p=22&vd_source=299f4bc123b19e7d6f66fefd8f124a03