好久没写设计模式相干的文章了, 最近看源码的时候碰到一些设计模式,这里就打算将这部分抽出来放在设计模式这个系列中。
什么是模板办法模式?
为了介绍什么是模板办法模式,咱们再次请出咱们的实习生小陈, 为了不波及公司的外围业务,咱们这里在讲述这个故事的时候,要屏蔽一下外围业务牵扯到具体的场景, 这一天领导给小陈安顿了一个这样的需要, 用代码模仿制作羊肉汤, 目前已知在制作羊肉的时候都会先荡涤,然而烧制办法是不同的,依据教训来说,将来会有更多的羊肉汤来进入到咱们的代码中。
小陈收到了这个需要,心里想既然荡涤的逻辑是独特的的,那这个简略,我就把荡涤抽出来为一个办法,而后再 service 外面写各自的初加工和烧制办法就能够了。于是写出的代码如下图所示:
/**
* 这是荡涤办法, 所有羊肉都要 荡涤
*/
public interface MuttonBaseMakeService {void waterMutton();
}
/**
* 再来一个实现类
*/
@Service
public class MuttonBaseMakeServiceImpl implements MuttonBaseMakeService{
@Override
public void waterMutton() {System.out.println("先荡涤羊肉");
}
}
/**
* 再来一个牛肉汤 service
*/
public interface MuttonSoupService{
/**
* 单县羊肉汤
*/
void danXianMuttonSoup();
/**
* 河南羊肉汤
*/
void heNanMuttonSoup();}
@Service
public class MuttonSoupServiceImpl implements MuttonSoupService{
@Autowired
private MuttonBaseMakeService muttonBaseMakeService;
@Override
public void danXianMuttonSoup() {muttonBaseMakeService.waterMutton();
System.out.println("单县牛肉汤");
}
@Override
public void heNanMuttonSoup() {muttonBaseMakeService.waterMutton();
System.out.println("河南牛肉汤");
}
}
感觉实现了工作, 测试也没问题之后,于是找到了领导,领导笑了笑,小陈想了想,上次领导我 HttpClient 怎么用的时候仿佛也是这个笑容 (参见 HTTP Client 学习笔记 ( 一) 初遇篇 ),心里想这预计是代码写的不行。
领导清了清嗓子,而后说道: 我这次调配给你的时候是讲过将来会接入很多的羊肉的做法的,你目前的写法是每接入一种羊肉的做法都是在接口中写,而后在对应的办法写实现对吗?小陈点了拍板,说道:是的。
领导接着问道: 每接入一种羊肉汤, 都必须写一遍 MuttonBaseMakeService 的 waterMutton 办法,这是第一点。第二点还记得大学的软件工程吗?软件工程绝对于其余工程的一个特点就是变动, 其实这个需要我保留了一部分,就是其实羊肉汤制作之后,打包动作是雷同的,那找你的思路是不是,每个羊肉汤办法都要加一个打包办法啊,咱们公司目前筹备接入二十种羊肉汤,那这改变量可不小,有没有其余办法优化一下呢?
小陈想了想,说道:是设计模式吗?
领导点了拍板: 要不咱上来搜一下。
通过一番搜寻小陈找到了模板办法模式,感觉很适应这个需要,模板办法模式的外围是:父类定义方法的逻辑骨架,而其中一些逻辑步骤的实现放到具体的子类中实现,这样能够做到不扭转逻辑构造的前提下定义某些细节的实现。于是将代码改成了如下实现:
/**
* 父类定义逻辑骨架
*/
public abstract class MuttonSoupBase {protected void water(){System.out.println("荡涤羊肉对立打包");
}
public void make(){water();
muttonSoup();
pack();}
/**
* 羊肉汤具体制作细节交给子类去实现
*/
protected abstract void muttonSoup();
protected void pack(){System.out.println("----- 打包 -----");
}
}
@Service
public class DanXianMuttonSoup extends MuttonSoupBase{
@Override
protected void muttonSoup() {System.out.println("----- 单县羊肉汤 ------");
}
}
@Service
public class HeNanMuttonSoup extends MuttonSoupBase{
@Override
protected void muttonSoup() {System.out.println("---- 河南羊肉汤 -------");
}
}
/**
* 简略应用示例
*/
@Service
public class SoupDemo {
@Autowired
private List<MuttonSoupBase> muttonSoupBases;
public void make() {for (MuttonSoupBase muttonSoupBase : muttonSoupBases) {muttonSoupBase.make();
}
}
}
小陈感觉代码没问题了, 就去找到领导。领导点了拍板,说道:能够,咱们公司长久层用的是 MyBatis,在 MyBatis 中用到了不少设计模式 其中就有模板办法模式,上面我带着你看一下,咱们看一下 MyBatis 中是如何使用模板办法模式的。
MyBatis 中的模板办法模式的使用
首先咱们 MyBatis 的基础架构是这样的:
留神核心层的 SQL 执行,在 MyBatis 外面你这是一个接口,咱们权且称之为 SQL 执行器,定义了根本的 SQL 执行行为。而后 BaseExecutor 实现了 Executor,也就是模板办法模式中的基类,定义逻辑骨架,具体的实现步骤由具体的子类来实现。如下图所示:
粗略的说 BatchExecutor 解决批量执行的 SQL 语句,批量插入、批量更新、批量删除这些,SimpleExecutor 是默认的 SQL 执行器,每次开启或者敞开都会创立一个 StatementHandler 对线个,ReuseExecutor 是重用 SQL 执行器,在某个 Statement 执行过之后,会将这个 Statement 缓存到一个 Map 中。领导还打算往下讲,忽然想到小陈只是一个实习生,笑了笑说道:你留神看 BaseExecutor 和 BatchExecutor、SimpleExecutor、ReuseExecutor 用的就是咱们下面探讨的模板办法模式,具体的查问、更新都交给对应的子类来执行,你缓缓领会一下,其实 MyBatis 外面模板形式用的还不少,再举一个例子 StatementHandler,StatementHandler 负责 MyBatis 和 JDBC 之间的交互,下面三个不同的执行器也对应三个不同的 StatementHandler, MyBatis 还是抉择定义了一个基类 BaseStatementHandler,三个子类 PreparedStatementHandler、CallableStatementHandler、SimpleStatementHandler 解决对应的 SQL。这是很经典的实现了,小陈你能够留神领会一下。
参考资料
- 玩转 MyBatis:深度解析与定制 https://juejin.cn/book/694491…