行为型设计模式 - 模板方法模式详解及其在Spring中的应用
基本介绍
模板方法模式(Template Method Pattern)也叫模板模式,它在一个抽象类中公开定义了执行它的方法的模板,它的字类可以按需重写方法实现,但调用将以抽象类中定义的方式进行。
简单来说,模板方法模式定义一个操作中的算法的骨架,将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就能重新定义该算法的某些特定步骤
模式结构
-
AbstractClass:抽象类,类中实现了模板方法,定义了算法的骨架
-
ConcreteClass:实现类,继承抽象类,重写其中的抽象方法
举例说明
编写一个制作豆浆的程序
制作豆浆的流程:选材(黄豆) → 添加配料 → 浸泡 → 磨豆浆
要求:通过添加不同的配料,可以制作出不同口味的豆浆(除了添加配料不同,其它步骤是相同的)
1、定义一个抽象类,BaseSoyMilk:豆浆类
public abstract class BaseSoyMilk {
/**
* 模板方法,制作豆浆的流程
* 声明为 final,防止子类重写
*/
protected final void make() {
select();
addOther();
soak();
grind();
}
private void select() {
System.out.println("选择上好的黄豆");
}
protected abstract void addOther();
private void soak() {
System.out.println("浸泡原材料");
}
private void grind() {
System.out.println("研磨成豆浆");
}
}
2、创建子类,PeanutSoyMilk:花生豆浆
public class PeanutSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
System.out.println("添加花生");
}
}
RedBeanSoyMilk:红豆豆浆
public class RedBeanSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
System.out.println("添加红豆");
}
}
3、创建测试类:Client
public class Client {
@Test
public void test() {
BaseSoyMilk peanutSoyMilk = new PeanutSoyMilk();
peanutSoyMilk.make();
System.out.println("****************");
BaseSoyMilk redBeanSoyMilk = new RedBeanSoyMilk();
redBeanSoyMilk.make();
}
}
4、运行结果
选择上好的黄豆
添加花生
浸泡原材料
研磨成豆浆
************
选择上好的黄豆
添加红豆
浸泡原材料
研磨成豆浆
钩子方法
在模板方法模式的抽象类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况来决定是否重写它,该方法称为「钩子」。
依然以上面制作豆浆的例子来说明,如果我们什么配料都不想加,制作纯豆浆,可以使用钩子方法对其改造。
1、为 BaseSoyMilk
类添加一个方法 isAdd
判断是否添加其它材料
public abstract class BaseSoyMilk {
/**
* 模板方法,制作豆浆的流程
* 声明为 final,防止子类重写
*/
protected final void make() {
select();
if(isAdd()){
addOther();
}
soak();
grind();
}
private void select() {
System.out.println("选择上好的黄豆");
}
protected abstract void addOther();
private void soak() {
System.out.println("浸泡原材料");
}
private void grind() {
System.out.println("研磨成豆浆");
}
protected boolean isAdd(){
return true;
}
}
2、创建一个子类 PureSoyMilk:纯豆浆
public class PureSoyMilk extends BaseSoyMilk {
@Override
protected void addOther() {
}
@Override
protected boolean isAdd() {
return false;
}
}
3、测试
@Test
public void test2() {
BaseSoyMilk pureSoyMilk = new PureSoyMilk();
pureSoyMilk.make();
}
4、运行结果(现在就没有添加其它材料的步骤了)
选择上好的黄豆
浸泡原材料
研磨成豆浆
模式分析
🎉 优点
- 算法只存在于父类中,易修改
- 实现了代码的复用性,父类的一些方法直接被子类使用
- 既统一了算法,也保证了很大的灵活性
💣 缺点
- 每一个不同的实现都需要一个子类来实现,导致类的数量增加,是系统更庞大
🎯 注意事项
- 一般模板方法要加上 final 修饰符,防止子类重写
🎲 使用场景
- 当我们要完成某个过程,该过程有一系列步骤,而且这些步骤基本相同,只有个别步骤的实现可能不同,可以考虑使用模板方法模式
在 Spring 源码中的应用
模板方法模式在 Spring IOC 容器初始化的时候有所应用,我们看一下 AbstractApplicationContext
中的 refresh
方法,它就是一个模板方法,里面调用了一系列方法,有以实现的具体方法,有未实现的抽象方法,也有空的钩子方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
//此方法内部调用了两个抽象方法refreshBeanFactory()和getBeanFactory()
//具体要取哪种beanFactory的控制权交给了子类
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
//钩子方法
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
//钩子方法
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
部分结构类图如下