设计模式学习笔记(18)模板方法

本文实例代码:https://github.com/JamesZBL/java_design_patterns

模板方法(Template Method) 模式是行为型模式的一种,通常的实现途径是定义一个模板抽象类,将需要具体实现的方法定义为抽象方法,由模板类的子类来重载实现。

所谓模板,就是事先定义好的,为了实现某个功能而专门定义的框架,它对其中的每个组成部分进行了规范。现实生活中,有许多和模板方法相似的例子,比如到银行办理业务要填写各种表格,表格的内容都是事先定义好的,客户只需按要求如实填写,经过工作人员的处理后就能办理相关的业务。这里的表格就是一种模板,需要用户填写的内容就是模板中的方法,业务的实现由用户填写的内容决定。

使用模板的好处是可以规范功能实现的整体模式,使功能的进一步实现变得很容易,所有模板的实现类都会按照模板的约束来实现具体的功能。

实例

我们在不同的状态下的学习效果是不一样的,现在我们需要对我们在不同状态下的学习效果进行展示,用一个抽象类定义这个功能的模板:

LearningMethod.java

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
public abstract class LearningMethod {

private static final Logger LOGGER = LoggerFactory.getLogger(LearningMethod.class);

/**
* 预习效果
*
* @return
*/
protected abstract String preLearning();

/**
* 状态
*
* @param description 学习状态
*/
protected abstract void Learning(String description);

/**
* 请教对象
*
* @param adviser 请教对象
*/
protected abstract void afterLearning(String adviser);

/**
* 学习过程
*
* @param description 听课状态
* @param adviser 请教对象
*/
public void learn(String description, String adviser) {
String preLearningResult = preLearning();
LOGGER.info("{}", preLearningResult);
Learning(description);
afterLearning(adviser);
}
}

心不在焉时的学习状态表述:

NegativeLearningMethod.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class NegativeLearinngMethod extends LearningMethod {

private static final Logger LOGGER = LoggerFactory.getLogger(NegativeLearinngMethod.class);

@Override
protected String preLearning() {
return "几乎没有预习,对上课要学的内容一无所知";
}

@Override
protected void Learning(String description) {
LOGGER.info("学习状态:{}", description);
}

@Override
protected void afterLearning(String adviser) {
if (!adviser.equals("")) {
LOGGER.info("只有很少的知识点没有听懂,于是找{}提问", adviser);
}
}
}

精力集中时的学习状态表述:

PositiveLearningMethod.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PositiveLearningMethod extends LearningMethod {

private static final Logger LOGGER = LoggerFactory.getLogger(PositiveLearningMethod.class);

@Override
protected String preLearning() {
return "预习到位,为听课打下很好的基础";
}

@Override
protected void Learning(String description) {
LOGGER.info("学习状态:{}", description);
}

@Override
protected void afterLearning(String adviser) {
if (!adviser.equals("")) {
LOGGER.info("只有很少的知识点没有听懂,于是找{}提问", adviser);
}
}
}

模拟一下两种学习状态的表述:

App.java

1
2
3
4
5
6
7
8
9
10
11
12
public class Application {

private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);

public static void main(String[] args) {
Student student = new Student(new PositiveLearningMethod());
student.learn("上课走神", "同学");
LOGGER.info("更换学习方法");
student.changeMethod(new NegativeLearinngMethod());
student.learn("认证听讲", "老师");
}
}

模板抽象类中对状态的表述进行了规范,描述状态由课前状态、上课状态以及课后状态组成,模板类暴露三个抽象方法留给子类实现,子类只需要分别重载这三个方法,给出具体的表述内容就可以了,并不需要关心整个表述过程是怎么进行的。

总结

通常我们如果能使用类的组合来实现功能的整合,就不要使用继承,因为这样可以避免类的数量失控,但是这个规则不适用于模板方法这个模式,因为它的设计初衷就是隐藏模板方法的部分实现细节,把功能的组成部分交由子类实现,所以继承在这里是必须要使用的。模板类将可复用的方法设计好,需要根据不同情况进行改动的方法,将需要具体实现的方法延迟到子类中实现。

模板方法的特点是:

  • 子类无需关注功能的整个实现流程或者规范,只需要填补模板中设定的各个空位置
  • 对于模板类,可以轻松的实现对整体流程的规范约束,这些约束体现在模板类中所有的模板方法上,子类和模板类的职责重点被划分开。

模板方法模式的应用场景有:

  • 某些复杂的算法需要分割成几部分,将其算法中固定不变的部分设计为模板方法和父类具体方法,把一些可能会变更的细节的方法交由其子类来实现。即:一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
  • 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。
分享到: