学习目标:
- 学会用模板模式梳理使用工作中流程标准化的业务场景。
- 了解JDK源码和Spring源码中对模板模式的运用。
内容定位:高级知识点,不太适合接触业务场景比较单一的开发者
模板模式的定义:
- 通常又叫模板方法模式(Template Method Pattern)是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。
- 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
- 属于行为型设计模式
模板模式的适用场景:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为被提取出来并集中收集到一个公共的父类中,从而避免代码重复。
模式对比:
抽象工厂模式是用接口实现的,当然也可以用抽象类,重点是归纳和总结对象的创建过程;
模板模式使用的是抽象类,在不改变流程的情况下,定制一些个性化的东西,达到出现不同结果的目的。
策略模式是只提供选择权,内部已经定义好所有的流程,没有干预逻辑的权利,关心的是结果;
模板模式关心整个流程实现的过程,可以微调流程和干预逻辑。
模板模式应用场景案例:
- AbstractList的get方法是抽象方法,通过子类实现的,比如ArrayList、LinkedList、ArrayQueue都有各自的get方法实现
- GenericServlet的service方法是抽象方法,被HttpServlet实现,划分请求种类的区分
- Mybatis源码中的BaseExecutor的doUpdate()、doFlushStatements()、doQuery()、doQueryCursor()方法,不同实现类BatchExecutor、SimpleExecutor、ReuseExecutor、ClosedExecutor。
生活场景案例
案例:网课老师备课需要固定的流程,但是不同的网课课后要求不一样,作业也不一样
网课抽象类
/** * @author: ZhouCong * @date: Create in 2021/1/11 15:23 * @description: 网课抽象类 模板会有一个或者多个未实现的方法,而且这几个未实现方法有着固定的执行顺序 */ public abstract class NetworkCourse { protected final void createCourse(){ // 1、发布预习资料 this.postPreResource(); // 2、制作PPT课件 this.createPPT(); // 3、在线直播 this.liveVideo(); // 4、提交课件、课堂笔记 this.postNode(); // 5、提交源码 this.postSource(); // 6、布置作业,有些课有作业有些课没有作业 // 如果有作业的话,检查作业,没有作业,完成了 if (needHomework()){ checkHomework(); } } abstract void checkHomework(); /** * 钩子方法: 作用实现流程微调 */ protected boolean needHomework(){ return false; }; final void postSource(){ System.out.println("提交源代码"); }; final void postNode(){ System.out.println("提交课件和笔记"); }; final void liveVideo(){ System.out.println("直播授课"); }; final void createPPT(){ System.out.println("创建备课PPT"); }; final void postPreResource(){ System.out.println("分发预习资料"); }; }
Java课
/** * @author: ZhouCong * @date: Create in 2021/1/11 15:34 * @description: */ public class JavaCourse extends NetworkCourse { @Override void checkHomework() { System.out.println("检查Java架构的作业"); } }
大数据课
/** * @author: ZhouCong * @date: Create in 2021/1/11 15:35 * @description: */ public class BigDataCourse extends NetworkCourse { /** * 是否有作业 */ private boolean needHomeworkFlag = false; public BigDataCourse(boolean needHomeworkFlag) { this.needHomeworkFlag = needHomeworkFlag; } @Override void checkHomework() { System.out.println("检查大数据的课后作业"); } @Override protected boolean needHomework() { return this.needHomeworkFlag; } }
测试
/** * @author: ZhouCong * @date: Create in 2021/1/11 15:40 * @description: */ public class NetworkCourseTest { public static void main(String[] args) { System.out.println("-------Java架构师课程-------"); NetworkCourse javaCourse = new JavaCourse(); javaCourse.createCourse(); System.out.println("-----大数据课程-----"); NetworkCourse bigDataCourse = new BigDataCourse(true); bigDataCourse.createCourse(); } }
运行结果
附上类结构图
业务场景案例
案例:简单实现spring的jdbcTemplate
RowMapper接口:结果集映射
/** * 功能描述: ORM映射定制化的接口 * * @Param: * @Return: * @Author: ZhouCong * @Date: 2021/1/11 15:55 */ public interface RowMapper<T> { T mapRow(ResultSet rs, int rowNum) throws Exception; }
JdbcTemplate
/** * @author: ZhouCong * @date: Create in 2021/1/11 15:55 * @description: */ public abstract class JdbcTemplate { private DataSource dataSource; public JdbcTemplate(DataSource dataSource) { this.dataSource = dataSource; } public List<?> executeQuery(String sql, RowMapper<?> rowMapper, Object[] values) { try { // 1、获取链接 Connection connection = this.getConnection(); // 2、创建语句集 PreparedStatement prepareStatement = this.createPrepareStatement(connection, sql); // 3、执行语句集 ResultSet rs = this.executeQuery(prepareStatement,values); // 4、处理结果集 List<?> result = this.paresResultSet(rs, rowMapper); // 5、关闭结果集 this.closeResultSet(rs); // 6、关闭语句集 this.closePreperStatement(prepareStatement); // 7、关闭连接 this.closeConnection(connection); return result; } catch (Exception e) { e.printStackTrace(); } return null; } protected void closeConnection(Connection connection) throws Exception { // 数据库连接池可以不关闭 connection.close(); } final void closePreperStatement(PreparedStatement prepareStatement) throws Exception { prepareStatement.close(); } final void closeResultSet(ResultSet rs) throws Exception { rs.close(); } protected List<?> paresResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception { List<Object> result = new ArrayList<>(); int rowNum = 0; while (rs.next()){ result.add(rowMapper.mapRow(rs,++rowNum)); } return result; } protected ResultSet executeQuery(PreparedStatement prepareStatement, Object[] values) throws Exception { for (int i = 0; i < values.length; i++) { prepareStatement.setObject(i+1,values[i]); } return prepareStatement.executeQuery(); } protected PreparedStatement createPrepareStatement(Connection connection, String sql) throws Exception { return connection.prepareStatement(sql); } public Connection getConnection() throws Exception { return this.dataSource.getConnection(); } }
Member实体类
/** * @author: ZhouCong * @date: Create in 2021/1/11 16:27 * @description: */ public class Member { private String username; private String password; private String nickname; private int age; private String address; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
MemberDao
/** * @author: ZhouCong * @date: Create in 2021/1/11 16:30 * @description: */ public class MemberDao extends JdbcTemplate { public MemberDao(DataSource dataSource) { super(dataSource); } public List<?> selectAll(){ String sql = "select * from t_member"; return super.executeQuery(sql, new RowMapper<Member>() { @Override public Member mapRow(ResultSet rs, int rowNum) throws Exception { Member member = new Member(); // 字段过多 用原型模式优化 member.setUsername(rs.getString("username")); member.setPassword(rs.getString("password")); member.setNickname(rs.getString("nickname")); member.setAge(rs.getInt("age")); member.setAddress(rs.getString("address")); return member; } },null); } }
测试
/** * @author: ZhouCong * @date: Create in 2021/1/11 16:48 * @description: */ public class MemberDaoTest { public static void main(String[] args) { MemberDao memberDao = new MemberDao(null); List<?> result = memberDao.selectAll(); System.out.println(result); } }
附上类结构图
总结
模板模式的优点:
- 提高代码的复用性:将相同的逻辑提取到父类中,将不同的代码逻辑让子类实现
- 提高代码的扩展性:提供代码逻辑流程的微调
- 符合开闭原则
模板模式的缺点:
- 类数目的增加:每一个抽象类都需要一个类来实现
- 间接的增加了系统的复杂度
- 继承关系自身的缺点:如果父类添加了新的抽象方法,所有的子类都要改一遍
以上对模板模式的介绍到此结束,欢迎批评指正。 附:源码地址