乐趣区

关于php:五分钟学会模板模式

概述

模板模式就是定义一个操作中的算法骨架,而后将一些步骤提早到子类中。模板办法使得子类在不扭转算法的构造即可重定义该算法的某些步骤。

应用场景

喝茶水

咱们都晓得泡茶根本步骤(算法骨架)有:

烧水、泡茶、喝茶水。

整个过程中很要害的步骤是泡茶,泡茶须要跑什么茶呢?泡多久?(留给子类本人去实现)。

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 中的模板办法模式的经典利用。

总结

模板办法模式就是定义了一个算法骨架,而后每个实现类本人去实现本人的业务逻辑。在 Spring、Mybatis、Dubbo 等框架中有很好实现案例。相对来说模板办法模式是算比较简单的哈,在面试中也能和面试官扯一会儿了。

「为了将来好一点 , 当初苦一点算什么」

退出移动版