概述
模板模式就是定义一个操做中的算法骨架,而后将一些步骤延迟到子类中。模板方法使得子类在不改变算法的结构便可重定义该算法的某些步骤。java
使用场景
喝茶水web
咱们都知道泡茶基本步骤(算法骨架)有:面试
烧水、泡茶、喝茶水。算法
整个过程当中很关键的步骤是泡茶,泡茶须要跑什么茶呢?泡多久?(留给子类本身去实现)。spring
APIjson
写过API
接口的码友们都知道,写API
通常有四个步骤:设计模式
参数解析、参数校验、处理业务、组织返回参数。api
把请求参数解析成该业务的请求参数json
解析成实体类;参数校验,您可使用通用的方式就是判断参数是否为空,也能够本身定义特殊的校验方式;处理业务通常每一个接口都是不同的,基本上都是本身去实现;至于返回参数,可能您得根据该API
接口业务来返回。缓存
支付订单springboot
作过支付相关的系统的人都清楚,支付订单大体分这三个步骤:
组织请求银行或者第三方支付公司的请求参数、发起支付、处理返回结果。
以上三个场景中的步骤就是算法骨架,至于每一个步骤可能每一个人喝茶偏好不同,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; } }
测试
这里使用的是idea
的Tools
下面的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
中的模板方法模式的经典应用。
总结
模板方法模式就是定义了一个算法骨架,而后每一个实现类本身去实现本身的业务逻辑。在Spring、Mybatis、Dubbo等框架中有很好实现案例。相对来讲模板方法模式是算比较简单的哈,在面试中也能和面试官扯一下子了。
「为了将来好一点 ,如今苦一点算什么」