好久没写设计模式相干的文章了, 最近看源码的时候碰到一些设计模式,这里就打算将这部分抽出来放在设计模式这个系列中。

什么是模板办法模式?

为了介绍什么是模板办法模式,咱们再次请出咱们的实习生小陈, 为了不波及公司的外围业务,咱们这里在讲述这个故事的时候,要屏蔽一下外围业务牵扯到具体的场景, 这一天领导给小陈安顿了一个这样的需要, 用代码模仿制作羊肉汤, 目前已知在制作羊肉的时候都会先荡涤,然而烧制办法是不同的,依据教训来说,将来会有更多的羊肉汤来进入到咱们的代码中。

小陈收到了这个需要,心里想既然荡涤的逻辑是独特的的,那这个简略,我就把荡涤抽出来为一个办法,而后再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...