【设计模式】模板模式,学会它咱也写出优雅健壮的代码!
作者:行百里er
博客:https://chendapeng.cn (opens new window)
提示
这里是 行百里er 的博客:行百里者半九十,凡事善始善终,吾将上下而求索!
# 强行代入
模板方法的定义:
Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
它是一种行为型模式。
这写的是啥?不急,我们先来假设一个场景,通过代码来看这说的是啥。
假设:
动物 Animal
这个抽象类,有新陈代谢 metabolism
这个方法,这个方法经过eat()、drink()和sleep()三个动作后执行完成。
小马 Pony
、小牛 Calf
继承 Animal
。
该假设场景的类图:
# 代码实现
/**
* 模板方法设计模式
* 范式重写的方法,系统帮我们自动调用的
* 都可以称之为模板方法
*/
public class Main {
public static void main(String[] args) {
Animal pony = new Pony();
pony.metabolism();
}
}
abstract class Animal {
/**
* 新陈代谢
* 假设都需要经过吃、喝、睡
*/
public void metabolism() {
eat();
drink();
sleep();
}
abstract void eat();
abstract void drink();
abstract void sleep();
}
class Pony extends Animal {
@Override
void eat() {
System.out.println("Pony eating");
}
@Override
void drink() {
System.out.println("Pony drinking");
}
@Override
void sleep() {
System.out.println("Pony sleeping");
}
}
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
39
40
41
42
43
44
45
46
如果再来一个小牛Calf
,它也是小动物,自然也可以继承自Animal
,如果main方法里调用小牛的新陈代谢方法呢?直接调用就完了,不需要逐一调用eat、drink和sleep了(自动在metabolism方法里调用)。
class Calf extends Animal {
@Override
void eat() {
System.out.println("Calf eating");
}
@Override
void drink() {
System.out.println("Calf drinking");
}
@Override
void sleep() {
System.out.println("Calf sleeping");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
调用:
public static void main(String[] args) {
Animal pony = new Pony();
pony.metabolism();
System.out.println("------------------------");
Animal calf = new Calf();
calf.metabolism();
}
2
3
4
5
6
7
运行结果:
Pony eating
Pony drinking
Pony sleeping
------------------------
Calf eating
Calf drinking
Calf sleeping
Process finished with exit code 0
2
3
4
5
6
7
8
9
OK,程序写完了,而且我用到了模板方法设计模式!
现在回过头来看一下,模板方法的定义:
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
小动物类Animal
的新陈代谢方法metabolism
就相当于一个算法骨架,我们的子类并没有改变算法(没有重写),只是对算法内部调用的方法继承下来,每个子类对这些特定步骤有自己的实现而已。
客户端调用的时候只需要调用骨架即可,具体的内部方法(eat、drink、sleep),系统会帮我们自动调用。
这种模式就是模板方法设计模式。
是的,我们一直在用它!
# 模板方法类图
这个时候,我们应该已经很熟悉模板方法了,类图很简单:
# 模板方法在Spring中的应用
# 1. JDBCTemplate
JDBCTemplate是Spring对JDBC的封装,开发人员自己写SQL,需要注入dataSource。
# 2. NamedParameterJdbcTemplate
也是基于JDBC的封装,不过在参数的书写上不使用 ?
占位符,而是使用 :参数名
的形式。
# 2. RestTemplate
RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。
# 3. AmqpTemplate
AmqpTemplate接口定义了发送和接收消息的所有基本操作。
# 4. AbstractBeanFactory
下面提供一个简化版的 AbstractBeanFactory 抽象类,该类实现了获取实例的 getBean 方法,在方法 getBean 的实现过程中可以看到,主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作。
getBean 并没有自身的去实现这些方法,而是只定义了调用过程以及提供了抽象方法,由实现此抽象类的其他类做相应实现。
/**
* 抽象类定义模板方法
* 继承了 DefaultSingletonRegistry ,也就具备了使用单例注册类方法
*
* @author 行百里者
* @date 2022-07-17 22:33
*/
public abstract class AbstractBeanFactory extends DefaultSingletonRegistry implements BeanFactory {
/**
* 获取实例
* 主要是对单例 Bean 对象的获取以及在获取不到时需要拿到 Bean 的定义做相应 Bean 实例化操作
* @param name 实例名称
* @return 实体类
* @throws BeansException 抛出 Bean 异常信息
*/
@Override
public Object getBean(String name) throws BeansException {
//获取单例类
Object bean = getSingleton(name);
if (bean != null) {
return bean;
}
//如果实体类为空,则创建实例(具备了创建实例的能力)
BeanDefinition beanDefinition = getBeanDefinition(name);
return createBean(name, beanDefinition);
}
/**
* 获取 Bean 定义
* 由实现此抽象类的其他类做相应的实现
* @param beanName Bean名称
* @return Bean 的定义信息
* @throws BeansException 抛出Bean异常
*/
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
/**
* 抽象方法:创建 Bean 实例
* 由实现此抽象类的其他类做相应的实现
* @param beanName Bean 名称
* @param beanDefinition Bean 的定义信息
* @return Bean 实体类
* @throws BeansException 抛出Bean异常
*/
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;
}
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
39
40
41
42
43
44
45
46
47
# 总结模板方法的特点
通过上面的例子,我们可以看到,这种模式的优点:
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
当然我们也能看到,对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
以上。
首发公众号 行百里er ,欢迎老铁们关注阅读指正。