概述
模板模式就是定义一个操作中的算法骨架,而后将一些步骤提早到子类中。模板办法使得子类在不扭转算法的构造即可重定义该算法的某些步骤。
应用场景
喝茶水
咱们都晓得泡茶根本步骤(算法骨架)有:
烧水、泡茶、喝茶水。
整个过程中很要害的步骤是泡茶,泡茶须要跑什么茶呢?泡多久?(留给子类本人去实现)。
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")@Controllerpublic 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")@RestControllerpublic 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
办法就是模板办法,源码为:
@Overridepublic 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
办法的实现
@Overridepublic 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等框架中有很好实现案例。相对来说模板办法模式是算比较简单的哈,在面试中也能和面试官扯一会儿了。
「为了将来好一点 ,当初苦一点算什么」