1. 适用场景

1)一次性实现一个算法的不变的部分,并将可变行为留给子类来实现。
2) 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复

模板模式可以帮助我们规范执行流程和执行逻辑,下面演示一个小demo

课程模板类:NetworkSource

  1. public abstract class NetworkCourse {
  2. protected final void createCourse(){
  3. //1. 发布预习资料
  4. this.postPreResource();
  5. //2. 制作ppt课件
  6. this.createPPT();
  7. //3. 在线直播
  8. this.liveVideo();
  9. //4. 提交课件 提交笔记
  10. this.postNote();
  11. //5. 提交源码
  12. this.postSource();
  13. //6. 布置作业 有些课没有作业 有些课有作业
  14. //如果有作业的话需要检查作业 如果没有作业 完成了
  15. if (needHomework()){
  16. checkHomework();
  17. }
  18. }
  19. abstract void checkHomework();
  20. //钩子方法 流程微调
  21. protected boolean needHomework(){ return false; }
  22. final void postSource(){
  23. System.out.println("提交源代码!");
  24. }
  25. final void postNote(){
  26. System.out.println("提交课件和笔记");
  27. }
  28. final void liveVideo(){
  29. System.out.println("在线授课!");
  30. }
  31. final void createPPT(){
  32. System.out.println("创建备课PPT");
  33. }
  34. final void postPreResource(){
  35. System.out.println("发布预习资料");
  36. }
  37. }

课程模板类是一个抽象类,为什么要用抽象类,主要是为了定义一些固定的流程,还有一些方法写成抽象方法可以作为个性化需求的使用,可以润色,可以自由编写,但是核心的模块是不能动的,这也是为什么不使用接口的原因!可以看到这里面流程化的步骤一共分为六大步,前五步都是固定的final不可变,只有第六步布置作业可以叫它钩子函数,可以选择!

java课程:JavaCourse

  1. public class JavaCourse extends NetworkCourse{
  2. @Override
  3. void checkHomework() {
  4. System.out.println("检查java的架构课件");
  5. }
  6. }

java课程类这里写的是没有布置作业,默认返回的needHomework为false

大数据课程类:BigDataSource

  1. public class BigDataSource extends NetworkCourse{
  2. private boolean needHomeworkFlag = false;
  3. public BigDataSource(boolean needHomeworkFlag) {
  4. this.needHomeworkFlag = needHomeworkFlag;
  5. }
  6. @Override
  7. void checkHomework() {
  8. System.out.println("检查大数据的课后作业!");
  9. }
  10. @Override
  11. protected boolean needHomework() {
  12. return this.needHomeworkFlag;
  13. }
  14. }

大数据课程类做了一些更改,布置了作业需要检查,所以这里重写了needHomework方法,我们后面可以传入true

NetworkSourceTest课程验证

  1. public class NetworkCourseTest {
  2. public static void main(String[] args) {
  3. System.out.println("----java架构师课程----");
  4. NetworkCourse javaCourse = new JavaCourse();
  5. javaCourse.createCourse();
  6. System.out.println("----大数据架构师课程----");
  7. NetworkCourse bigDataSource = new BigDataSource(true);
  8. bigDataSource.createCourse();
  9. }
  10. }

可以看到在大数据传值的时候,传入了是否需要布置作业为true!
运行结果:

  1. ----java架构师课程----
  2. 发布预习资料
  3. 创建备课PPT
  4. 在线授课!
  5. 提交课件和笔记
  6. 提交源代码!
  7. ----大数据架构师课程----
  8. 发布预习资料
  9. 创建备课PPT
  10. 在线授课!
  11. 提交课件和笔记
  12. 提交源代码!
  13. 检查大数据的课后作业!

这样流程化的东西并没有被改变,针对于不同的课程做一些微调,实现了模板的效果,想想如果换成接口的话,所有的流程全部重写,那么也就不是模板了!另外提醒一下钩子方法:提供对流程的微调

2. JDBC模板

jdbc我们已经很熟悉了,其实底层使用了模板模式进行sql语句的增删改查,只不过对部分入参进行了过滤!
下面我们尝试着编写模拟的jdbc模板,从数据库连接到存入数据库,它主要分为几个步骤:

  1. 获取数据库连接
  2. 编写sql
  3. 执行sql语句
  4. 处理结果集
  5. 关闭结果集
  6. 关闭语句集
  7. 关闭连接

JdbcTemplate模板

  1. public abstract class JdbcTemplate {
  2. private DataSource dataSource;
  3. public JdbcTemplate(DataSource dataSource){
  4. this.dataSource = dataSource;
  5. }
  6. public List<?> execteQuery(String sql,RowMapper<?> rowMapper,Object[] values){
  7. try {
  8. //1.获取链接
  9. Connection conn = this.getConnection();
  10. //2.创建语句集
  11. PreparedStatement pm = this.createPrepareStatement(conn,sql);
  12. //3.执行语句
  13. ResultSet rs = this.execteQuery(pm,values);
  14. //4.处理结果集
  15. List<?> result = this.pareseResultSet(rs,rowMapper);
  16. //5.关闭结果集
  17. this.closeResultSet(rs);
  18. //6.关闭语句集
  19. this.closeStatement(pm);
  20. //7.关闭链接
  21. this.closeConnection(conn);
  22. return result;
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26. return null;
  27. }
  28. protected void closeConnection(Connection conn) throws SQLException {
  29. //数据库连接池 不关闭
  30. conn.close();
  31. };
  32. protected void closeStatement(PreparedStatement pm) throws SQLException {
  33. pm.close();;
  34. };
  35. protected void closeResultSet(ResultSet rs) throws SQLException {
  36. rs.close();
  37. };
  38. protected List<?> pareseResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception {
  39. //迭代
  40. List<Object> result = new ArrayList<>();
  41. int rowNum = 1;
  42. while (rs.next()){
  43. result.add(rowMapper.mapRow(rs,rowNum++));
  44. }
  45. return result;
  46. };
  47. private ResultSet execteQuery(PreparedStatement pm, Object[] values) throws SQLException {
  48. for (int i=0;i< values.length;i++){
  49. pm.setObject(i,values);
  50. }
  51. return pm.executeQuery();
  52. }
  53. protected PreparedStatement createPrepareStatement(Connection conn, String sql) throws SQLException {
  54. return conn.prepareStatement(sql);
  55. }
  56. public Connection getConnection() throws SQLException {
  57. return this.dataSource.getConnection();
  58. };
  59. }

jdbc模板中核心方法为execteQuery,里面包含了几个核心步骤!几个参数需要注意一下
sql:核心执行sql
rowMapper:这个用于对象封装传入的参数,这个对象会从rs中取出,完成rowMapper封装
values:默认为null,可传入参数

RowMapper<T> _orm映射定制化的接口_

  1. public interface RowMapper<T> {
  2. T mapRow(ResultSet rs,int rowNum) throws Exception;
  3. }

定制化映射的接口,很明显是对结果集进行一对一的映射,方便后续的对象一对一复制转化!

Member属性类

  1. public class Member {
  2. private String username;
  3. private String password;
  4. private String nickName;
  5. private int age;
  6. private String addr;
  7. public String getUsername() {
  8. return username;
  9. }
  10. public void setUsername(String username) {
  11. this.username = username;
  12. }
  13. public String getPassword() {
  14. return password;
  15. }
  16. public void setPassword(String password) {
  17. this.password = password;
  18. }
  19. public String getNickName() {
  20. return nickName;
  21. }
  22. public void setNickName(String nickName) {
  23. this.nickName = nickName;
  24. }
  25. public int getAge() {
  26. return age;
  27. }
  28. public void setAge(int age) {
  29. this.age = age;
  30. }
  31. public String getAddr() {
  32. return addr;
  33. }
  34. public void setAddr(String addr) {
  35. this.addr = addr;
  36. }
  37. @Override
  38. public String toString() {
  39. return "Member{" +
  40. "username='" + username + '\'' +
  41. ", password='" + password + '\'' +
  42. ", nickName='" + nickName + '\'' +
  43. ", age=" + age +
  44. ", addr='" + addr + '\'' +
  45. '}';
  46. }
  47. }

Member不用多说,测试用例的属性类

dao层的MemberDao

  1. public class MemberDao extends JdbcTemplate {
  2. public MemberDao(DataSource dataSource) {
  3. super(dataSource);
  4. }
  5. public List<?> selectAll(){
  6. String sql = "select* from t_member";
  7. return super.execteQuery(sql, new RowMapper<Member>() {
  8. @Override
  9. public Member mapRow(ResultSet rs, int rowNum) throws Exception {
  10. Member member = new Member();
  11. member.setUsername(rs.getString("username"));
  12. member.setPassword(rs.getString("password"));
  13. member.setAge(rs.getInt("age"));
  14. member.setAddr(rs.getString("addr"));
  15. member.setNickName(rs.getString("nickName"));
  16. return member;
  17. }
  18. },null);
  19. }
  20. }

这个核心dao就是我们平常使用的调用数据库层的接口,其实这里也就是对数据包括参数和对象的封装,然后传给jdbc模板进行数据库查询,返回结果集!这样一个jdbc模板我们就差不多写完了,可能我们平常写模板类的场景确实很少,大多数我们所看到的都是业务之间的问题,只有一些底层实现会注重模板的编写,了解并看清本质。

3. 模板模式的缺点

  1. 类数目的增加
  2. 间接地增加了系统实现的复杂度
  3. 继承关系自身缺点,如果父类关系添加新的抽象方法,所有子类都要改一遍