设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

  1. 定义
  2. 优点
  3. 缺点
  4. 应用场景
  5. 使用场景
  6. 钩子方法
  7. 场景举例

定义

定义一个操作中的算法骨架,将算法的一些子步骤延伸到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤

优点

1、封装不变部分,扩展可变部分,不变的封装到父类实现,可变的由子类去扩展

2、便于代码复用,易于拓展,符合开闭原则

缺点

1、父类的方法由子类实现,是一种反向控制,提高代码阅读难度

应用场景

使用spring的都用过

1
2
ApplicationContext ac = new ClassPathXmlApplicationContext(“classpath:/*.xml”);
ac.getBean("bean")

这个类的就是模板方法子类的一种体现,通过不断的继承重写父类方法,子类所需要实现的方法越来越少,也可以看出该模式是一种反向控制模式

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
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();
contextRefresh.end();
}
}
}

使用场景

1、算法的整体步骤很固定,但其中个别部分易变时

2、当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,就可以只允许进行部分扩展

钩子方法

钩子方法源于设计模式中模板方法模式,模板方法模式中分为两大类:模版方法和基本方法,而基本方法又分为:抽象方法,具体方法,钩子方法

对于抽象方法或者接口中定义的方法的一个空实现,在实际中的应用,比如说有一个接口,而你只想用其中一个方法,那么可以写一个抽象类实现这个接口,在这个抽象类里将你要用的那个方法设置为abstract,其它方法进行空实现,然后你再继承这个抽象类,就不需要实现其它不用的方法,这就是钩子方法的作用

场景举例

现在小王需要在公司每周写周报,而周报的格式基本上又不变,在中间修改部分内容即可,假如平常按照总分总的结构写,偶尔加一些图片什么的,那么抽象类可以如此实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public abstract class Weekly {
//防止子类重写,相当于算法骨架
final void write() {
start();
if (hook()) {
addOther();
}
content();
end();
}

void start() {
System.out.println("周报开头");
}

void content() {
System.out.println("周报正文");
}

void end() {
System.out.println("周报开头");
}

//添加其他一些东西
abstract void addOther();

//默认不添加
boolean hook() {
return false;
}
}

A同事比较懒,只实现公司要求的基本方法

1
2
3
4
5
6
public class LazyPeople extends Weekly {
@Override
void addOther() {
//空实现
}
}

B同事比较喜欢表现

1
2
3
4
5
6
7
8
9
10
11
12
public class SmartPeople extends Weekly {
@Override
void addOther() {
System.out.println("加些图片,加点论证");
}

//重写钩子,使父类调用
@Override
boolean hook(){
return true;
}
}

测试

1
2
new LazyPeople().write();
new SmartPeople().write();

输出

1
2
3
4
5
6
7
周报开头
周报正文
周报开头
周报开头
加些图片,加点论证
周报正文
周报开头