好久没写设计模式相干的文章了, 最近看源码的时候碰到一些设计模式,这里就打算将这部分抽出来放在设计模式这个系列中。
什么是模板办法模式?
为了介绍什么是模板办法模式,咱们再次请出咱们的实习生小陈, 为了不波及公司的外围业务,咱们这里在讲述这个故事的时候,要屏蔽一下外围业务牵扯到具体的场景, 这一天领导给小陈安顿了一个这样的需要, 用代码模仿制作羊肉汤, 目前已知在制作羊肉的时候都会先荡涤,然而烧制办法是不同的,依据教训来说,将来会有更多的羊肉汤来进入到咱们的代码中。
小陈收到了这个需要,心里想既然荡涤的逻辑是独特的的,那这个简略,我就把荡涤抽出来为一个办法,而后再service外面写各自的初加工和烧制办法就能够了。于是写出的代码如下图所示:
/** * 这是荡涤办法,所有羊肉都要 荡涤 */public interface MuttonBaseMakeService { void waterMutton();}/** * 再来一个实现类 */@Servicepublic class MuttonBaseMakeServiceImpl implements MuttonBaseMakeService{ @Override public void waterMutton() { System.out.println("先荡涤羊肉"); }}/** * 再来一个牛肉汤service */public interface MuttonSoupService{ /** * 单县羊肉汤 */ void danXianMuttonSoup(); /** * 河南羊肉汤 */ void heNanMuttonSoup();}@Servicepublic 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("-----打包-----"); }}@Servicepublic class DanXianMuttonSoup extends MuttonSoupBase{ @Override protected void muttonSoup() { System.out.println("-----单县羊肉汤------"); }}@Servicepublic class HeNanMuttonSoup extends MuttonSoupBase{ @Override protected void muttonSoup() { System.out.println("---- 河南羊肉汤-------"); }}/** * 简略应用示例 */@Servicepublic 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...