快速掌握模板方法模式

概述

模板模式就是定义一个操作中的算法骨架,然后将一些步骤延迟到子类中。模板方法使得子类在不改变算法的结构即可重定义该算法的某些步骤。

使用场景

泡茶

我们都知道泡茶基本步骤有:

烧水、选茶叶、泡茶、喝茶水。

这里指的是一般步骤。至于你烧水时用什么烧?热得快、热水壶、还是其他工具,反正目的是把水烧开就好;你选的茶叶是什么茶叶?铁观音、菊花茶、龙井茶等,您想选什么茶喜欢就好;泡茶都是一样的,就是把茶叶放开水里泡,甚至您也可以选择自己泡茶时间的长久或者选择水的温度;至于喝茶是大杯或者还是小杯自己喜欢就好。

API

写过API接口的码友们都知道,写API一般有四个步骤:

参数解析、参数校验、处理业务、组织返回参数。

把请求参数解析成该业务的请求参数json解析成实体类;参数校验,您可以使用通用的方式就是判断参数是否为空,也可以自己定义特殊的校验方式;处理业务一般每个接口都是不一样的,基本上都是自己去实现;至于返回参数,可能您得根据该API接口业务来返回。

支付订单

做过支付相关的系统的人都清楚,支付订单大致分这三个步骤:

组织请求银行或者第三方支付公司的请求参数、发起支付、处理返回结果。

以上三个场景中的步骤就是算法骨架,至于每个步骤可能每个人喝茶偏好不一样,API接口业务不一样、银行或者第三方支付的支付处理不一样,可能需要自己做特殊的处理。

场景现实

实现一个API接口

算法类

package com.tian.springbootdemo.controller;
import com.tian.springbootdemo.rep.Result;
/**
 * @auther: 老田
 * @Description: 模板类
 */
public abstract class AbstractTemplate {

/**
     * 算法骨架
     */
    public Result execute() {
        //第一步:解析参数
        parseRequestParameters();
        //第二步:校验参数
        checkRequestParameters();
        //第三步:业务处理
        Object data= doBusiness();
        //第四步:组织返回参数
        return assembleResponseParameters(data);
    }

/**
     * 解析参数
     */
    public abstract void parseRequestParameters();

/**
     * 校验参数
     */
    public abstract void checkRequestParameters();

/**
     * 业务处理
     */
    public abstract Object doBusiness();

/**
     * 组织返回参数
     */
    public abstract Result assembleResponseParameters(Object object);
}

实现类一

import com.tian.springbootdemo.rep.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @auther: 老田
 * @Description: api接口
 */
@RequestMapping("/api")
@Controller
public class MyApiController extends AbstractTemplate {

@RequestMapping(value = "/users", method = RequestMethod.POST)
    @ResponseBody
    @Override
    public Result execute() {
        return super.execute();
    }

@Override
    public void parseRequestParameters() {
        System.out.println("*****解析参数*****");
    }

@Override
    public void checkRequestParameters() {
        System.out.println("*****校验参数*****");
    }

@Override
    public Object doBusiness() {
        System.out.println("*****处理业务*****");
        // TODO: 2018/11/17 调用service处理业务
        User user = new User();
        user.setName("小田哥");
        user.setId(1);
        user.setAge(20);
        user.setSex("man");
        return user;
    }

@Override
    public Result assembleResponseParameters(Object object) {
        System.out.println("*****返回参数*****");
        Result result = new Result("200", "处理成功");
        result.setData(object);
        return result;
    }
}

实现类二

import com.tian.springbootdemo.dao.domain.User;
import com.tian.springbootdemo.rep.Result;
import org.springframework.web.bind.annotation.*;

/**
 * @auther: 老田
 * @Description: api接口
 */
@RequestMapping("/api")
@RestController
public class LoginController extends AbstractTemplate {
    @PostMapping(value = "/login")
    @Override
    public Result execute() {
        return super.execute();
    }
    @Override
    public void parseRequestParameters() {
        System.out.println("解析登录参数");
    }

@Override
    public void checkRequestParameters() {
        System.out.println("校验登录用户名是否为空,密码是否为空");
    }

@Override
    public Object doBusiness() {
        System.out.println("通过用户名查询是否存在此用户");
        System.out.println("校验用户密码是否正确");
        System.out.println("登录成功");
        User user = new User();
        user.setName("小田哥");
        user.setId(1);
        user.setAge(20);
        user.setSex("man");
        return user;
    }

@Override
    public Result assembleResponseParameters(Object object) {
        System.out.println("*****返回参数*****");
        Result result = new Result("200", "登录成功");
        result.setData(object);
        return result;
    }
}

相关类

/**
 * @auther: 老田
 * @Description: 返回信息
 */
public class Result {
    //返回码
    private String responseCode;
    //描述
    private String message;
    //数据
    private Object data;

public Result() {
    }

public Result(String responseCode, String message) {
        this.responseCode = responseCode;
        this.message = message;
    }

public Result(String responseCode, String message, Object data) {
        this.responseCode = responseCode;
        this.message = message;
        this.data = data;
    }

public String getResponseCode() {
        return responseCode;
    }

public void setResponseCode(String responseCode) {
        this.responseCode = responseCode;
    }

public String getMessage() {
        return message;
    }

public void setMessage(String message) {
        this.message = message;
    }

public Object getData() {
        return data;
    }

public void setData(Object data) {
        this.data = data;
    }
}
import java.io.Serializable;

/**
 * @auther: 老田
 * @Description: 数据
 */
public class User implements Serializable {
    //id
    private Integer id;
    //用户姓名
    private String name;
    //性别
    private String sex;
    //年龄
    private int age;

public User() {
    }

public User(Integer id, String name, String sex, int age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

public Integer getId() {
        return id;
    }

public void setId(Integer id) {
        this.id = id;
    }

public String getName() {
        return name;
    }

public void setName(String name) {
        this.name = name;
    }

public String getSex() {
        return sex;
    }

public void setSex(String sex) {
        this.sex = sex;
    }

public int getAge() {
        return age;
    }

public void setAge(int age) {
        this.age = age;
    }
}

测试

这里使用的是ideaTools下面的REST Client进行接口测试:

enter image description here
enter image description here

再看看控制台Console打印出来的信息:

enter image description here
enter image description here

这样我们就把模板设计模式应用到我们的具体代码里了,同样的我们也可以实现其他API的实现类。

另外,参数校验也可以在 AbstractTemplate  中实现一个 default   的方式,比如说:校验参数是否为空,但是子类也可以重写这个方法,自己做一个特殊的校验;比如说:如果参数中有手机号码,那么我们不仅要校验手机号是否为空,还可以校验这个手机号码是不是11位,是否合法的校验等等。

模板模式优缺点

优点
  • 提高代码的复用性,将相同部分的代码放到抽象类里;

  • 提高拓展性,将不同的放到不同的实现类里,通过实现类的扩展增加一些自己需要的行为;

  • 实现反向控制,通过一个父类调用实现类的操作,通过对实现类的扩展增加新行为,实现反向控制。

缺点
  • 因为引入了抽象类,每个不同的实现都需要一个子类来现实,这样会导致类的数量增多,从而导致系统实现的复杂度。

大佬们在框架里是怎么使用的?

Spring中

AbstractApplicationContext 中的refreash方法就是模板方法,源码为:

@Override
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) { 
            //调用容器准备刷新的方法,获取容器的当时时间,
            //同时给容器设置同步标识
            prepareRefresh();
            //告诉子类启动refreshBeanFactory()方法,
            //Bean定义资源文件的载入从
            //子类的refreshBeanFactory()方法启动
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            //为BeanFactory配置容器特性,例如类加载器、事件处理器等
            prepareBeanFactory(beanFactory);
            try { 
                //为容器的某些子类指定特殊的BeanPost事件处理器
                //-----子类实现
                postProcessBeanFactory(beanFactory);
                //调用所有注册的BeanFactoryPostProcessor的Bean
                invokeBeanFactoryPostProcessors(beanFactory);
                //为BeanFactory注册BeanPost事件处理器.
                //BeanPostProcessor是Bean后置处理器,
                //用于监听容器触发的事件
                registerBeanPostProcessors(beanFactory);
                //初始化信息源,和国际化相关.
                initMessageSource();
                //初始化容器事件传播器.
                initApplicationEventMulticaster();
                //调用子类的某些特殊Bean初始化方法
                //-----子类实现
                onRefresh();
                //为事件传播器注册事件监听器.
                registerListeners();
                //初始化所有剩余的单例Bean
                finishBeanFactoryInitialization(beanFactory);
                //初始化容器的生命周期事件处理器,
                //并发布容器的生命周期事件
                finishRefresh();
                //.....

该方法就是上下文启动模板方法。这就是模板模式在Spring中应用场景之一。

Mybatis中

BaseExecutor中的update方法就是一个模板方法

 /**
     * SqlSession.update/insert/delete会调用此方法
     * 模板方法
     */
    @Override
    public int update(MappedStatement ms, Object parameter) throws SQLException {
         ErrorContext.instance().resource(ms.getResource()).activity("executing an                update").object(ms.getId());
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        //先清局部缓存,再更新,如何更新交由子类,
        //模板方法模式
        clearLocalCache();
        //由子类实现(钩子方法)
        return doUpdate(ms, parameter);
    }

BaseExecutor里只是定义了方法,但是实现是在子类里

//更新 
protected abstract int doUpdate(MappedStatement ms, Object parameter)
                                                     throws SQLException;
//查询
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds 
                    rowBounds, ResultHandler resultHandler, BoundSql boundSql)
                   throws SQLException;
 //...do开头的方法都是交给具体子类自己去实现

BaseExecutor的实现类如下:

enter image description here

实现类SimpleExecutor中的doUpdate方法的实现

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //新建一个StatementHandler
      //这里看到ResultHandler传入的是null
      StatementHandler handler = configuration.newStatementHandler(
          this, ms, parameter,          RowBounds.DEFAULT, null, null);
      //准备语句
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler.update
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
}

实现类ReuseExecutor中的doUpdate方法的实现

@Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
     //和SimpleExecutor一样,
     //新建一个StatementHandler
     //这里看到ResultHandler传入的是null
     StatementHandler handler = configuration.newStatementHandler(
             this, ms, parameter,       RowBounds.DEFAULT, null, null);
     //准备语句
     Statement stmt = prepareStatement(handler, ms.getStatementLog());
     return handler.update(stmt);
  }

这就是Mybatis中的模板方法模式的应用。

另外相关实现类代码由于篇幅过了,此处做省略,有兴趣的朋友可以联系我,咱们一起看源码。

(0)

相关推荐

  • JAVA中equals的用法

    Object类中的equals方法用于检测一个对象是否等于另一个对象.在Object类中,这个方法判断两个对象是否具有相同的引用,如果两个对象具有相同的引用,它们一定是相等的.从这点上看,将其作为默认 ...

  • 三分钟阅读,快速了解专业模式使用方法

    喜欢手机摄影的朋友对于华为手机肯定不会觉得陌生,华为手机有这么一个模式,它是专门为喜欢拍照的朋友量身定做的模式,那就是专业模式. 专业模式如何使用?里面的参数如何设置?今天爱拍大叔就大家讲解一下这个专 ...

  • #资本 只会投资圈内人,连锁企业用什么商业模式可以快速#上市 #商业模式

    #资本 只会投资圈内人,连锁企业用什么商业模式可以快速#上市 #商业模式

  • 【4/25】在页面对象中启用模板方法模式(Template Method Pattern)

    这是<小游戏从0到1设计模式重构>系列内容第4篇,所有源码及资料在"程序员LIYI"公号回复"小游戏从0到1"获取. 上一小节我们应用了组合模式,对 ...

  • PHP设计模式之模板方法模式

    PHP设计模式之模板方法模式 模板方法模式,也是我们经常会在不经意间有会用到的模式之一.这个模式是对继承的最好诠释.当子类中有重复的动作时,将他们提取出来,放在父类中进行统一的处理,这就是模板方法模式 ...

  • [PHP小课堂]PHP设计模式之模板方法模式

    [PHP小课堂]PHP设计模式之模板方法模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 蔚来的快速换电模式的前景如何?

    随着电动汽车保有量的提升,车辆续航也成为了很多人所关注的,续航里程依旧是众多车主的焦虑,为解决车辆的续航焦虑,各大车企也在不断的进行研究,从慢充到快充到换电等方式来满足车辆充电续航的需求,而车辆的换电 ...

  • 春天了蟹爪兰不长新叶?浇“2种水”试试,快速开启“生长模式”

    蟹爪兰是一种家庭中养的比较多的观花植物,它是属于仙人掌科的,养护起来比较简单,而且它能够从秋季一直持续开,开到春季来临,花期也是相当长的,但是只要进入春季以后,大部分的蟹爪兰开完花以后就会快进入一个快 ...

  • 怎么开启快速进步的模式。

    人的一生,可以是佛系的一生,也可以是战斗的一生,如果要选择后者,就要全盘的设计,专门的运营,只有刻意,才是努力. 一.做正确的事情.高效的团队和个人,一定要有一种特质,就是充满狼性,一心扑在抢食事业上 ...

  • TemplateMethodPattern模板方法模式

    模板方法模式 1.定义 定义一个操作中的算法的框架,而将一些步骤的实现延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 使用模板方法模式制造两款汽车.定义汽车必须有的特质 ...