模板方法模式(Template Method): 顾名思义, 它定义了一个模板, 只用填写模板上的某些内容即可, 类似于填空题或者表格填写. 具体定义为: 定义了一个操作中, 某个部分的骨架, 把变动部分放到子类去实现, 不改变骨架的前提下, 子类可以灵活拓展, 是一种行为型的设计模式.
设计模式-概述
结构
这玩意有这么几个部分组成:
- 抽象类(AbstractClass) : 定义了一个程序执行的骨架, 填空题的题干.
- 具体子类(ConcreteClass) : 定义了骨架中缺失的部分, 需要填写的空.
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
| @startuml template-method-1 !include ./common.cuml
Class(AbstractClass,"抽象类") << abstract >> { + abstractMethod1() : void abstract + abstractMethod2() : void abstract + specificMethod1() : void + specificMethod2() : void + specificMethod3() : void + mainMethod() : void }
Class(ConcreteClassA,"子类A") { + abstractMethod1() : void + abstractMethod2() : void }
Class(ConcreteClassB,"子类B") { + abstractMethod1() : void + abstractMethod2() : void }
note as N1 public void mainMethod() { specificMethod1(); abstractMethod1(); specificMethod2(); abstractMethod2(); specificMethod3(); } end note
AbstractClass <|.. ConcreteClassA AbstractClass <|.. ConcreteClassB N1 .. AbstractClass
@enduml
|
代码实现
- 有一个父类, 定义了一个模板方法
mainMethod()
, 方法中有其他5个方法, 3个是骨架方法, 2个是拓展方法(交给子类实现)
- 有两个子类, 实现了父类的方法, 各自的实现不同.
- 执行者, 使用不同的子类, 执行
mainMethod()
方法, 可以发现:执行过程中, 有两步操作是由子类定义的.
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class Main2 { public static void main(String[] args) { AbstractClass a = new ConcreteClassA(); a.mainMethod(); AbstractClass b = new ConcreteClassB(); b.mainMethod(); } }
abstract class AbstractClass { public abstract void abstractMethod1();
public abstract void abstractMethod2();
private void specificMethod1() { System.out.println("AbstractClass.specificMethod1"); }
private void specificMethod2() { System.out.println("AbstractClass.specificMethod2"); }
private void specificMethod3() { System.out.println("AbstractClass.specificMethod3"); }
void mainMethod() { specificMethod1(); abstractMethod1(); specificMethod2(); abstractMethod2(); specificMethod3(); } }
class ConcreteClassA extends AbstractClass {
@Override public void abstractMethod1() { System.out.println("ConcreteClassA.abstractMethod1"); }
@Override public void abstractMethod2() { System.out.println("ConcreteClassA.abstractMethod2"); } }
class ConcreteClassB extends AbstractClass {
@Override public void abstractMethod1() { System.out.println("ConcreteClassB.abstractMethod1"); }
@Override public void abstractMethod2() { System.out.println("ConcreteClassB.abstractMethod2"); } }
|
使用场景
- 这个设计模式适用于整体步骤很固定的, 不会有变动, 比如同试卷的同道填空题, 大家都是一样的, 不会出现每个人各一种题目.
- 当各个同级子类中, 有相似的执行流程.
- 仅仅在流程中的某一步需要自定义.
- 并且这样的子类有很多, 或者未来会有很多时.
具体应用
我的任职公司(轻轻教育), 有个地方需要对接第三方API, 调用他们的各种智能分析, 各种分析任务流程大致是相同的(任务入队
->并发控制
->任务出队
->修改状态为执行中
->调用第三方执行
->获取结果
->判断是否获取成功
->保存结果到各自表
->任务标记结果
), 唯一不同的地方有两个:一个是调用第三方执行
, 另外一个是:保存结果到各自表
. 这里用到了模板方法模式, 父类封装了大致流程, 暴露两个abstract
的方法, 交给子类实现, 各个子类中submit()
方法用来拼不同参数并调用不同的接口, saveResult()
方法用来获取到一个结果, 并保存.
Mybatis
的BaseExecutor
接口中的模板方法模式:
BaseExecutor
中主要提供了缓存管理和事务管理的基本功能, 有4个子类:
SimpleExecutor
执行Mapper语句时默认使用的
ReuseExecutor
提供了 Statement 重用的功能,通过 statementMap 字段缓存使用过的 Statement 对象进行重用,可以减少SQL预编译以及创建和销毁 Statement 对象的开销,从而提高性能
BatchExecutor
实现了批处理多条SQL语句的功能,在客户端缓存多条SQL并在合适的时机将多条SQL打包发送给数据库执行,从而减少网络方面的开销,提升系统的性能
ClosedExecutor
只是某个类的一个内部类