设计模式之 ==> 模板方法模式
一、什么是模板方法模式
定义一个操作中的算法骨架,将一些步骤的具体实现延迟到子类当中去实现。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定的步骤。
二、模板方法模式的使用场景
- 在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。
- 程序主框架相同,细节不同的情况下,也可以使用模板方法。
三、模板方法模式的写法举例
下面我们以一个使用 JDBC 查询 MySQL 数据库的例子来介绍一下模板方法模式,首先我们来看一下最初版的实现:
public class UserQuary { private static final String URL = "jdbc:mysql://xxx.xxx.xxx.xxx:3306/jdbc"; private static final String USER_NAME = "xxxx"; private static final String PASSWORD = "xxxxxx"; public List<User> quary(String sql, List<Object> params) throws Exception { // 1.建立连接 Connection connection = DriverManager.getConnection(URL, USER_NAME, PASSWORD); // 2.建立Statement,并将数据注入预编译sql当中 PreparedStatement statement = connection.prepareStatement(sql); for (int i = 0; i < params.size(); i++) { statement.setObject(i + 1, params.get(i)); } // 3.执行sql,获取结果 ResultSet resultSet = statement.executeQuery(); // 4.处理查询结果,将查询结果转换为对应的User对象 List<User> users = new ArrayList<>(); while (resultSet.next()) { User user = User.builder() .id(resultSet.getLong("id")) .name(resultSet.getString("account_id")) .age(resultSet.getInt("account_name")) .build(); users.add(user); } // 5.资源关闭 resultSet.close(); statement.close(); connection.close(); // 返回结果 return users; } }
从以上代码可以看出,这个方法只能查询 user 表,如果我们需要查询其他的表,需要 copy 很多份,在处理查询结果的时候根据对应的表处理成其对应的实体类对象。我们整段代码发现其中总共五个步骤,其中有四个步骤(1,2,3,5)是固定不变的,只有第 4 步对于查询结果的处理,对于不同的表处理的方式不同,对应的实体类模板也不同,那么这就很符合我们上面对模板方法模式的使用场景,我们在父类中实现 1,2,3,5 这四个步骤,第 4 步交给子类去处理。下面我们来进行设计和实现:
首先,定义一个接口 DBHandler,描述查询方法:
public interface DBHandler<T> { /** * 参数说明: * @param sql --> "select * from tableName where column1 = ? and column2 = ?" * @param params --> ["jack", "male"] */ List<T> quary(String sql, List<Object> params); }
由于具体不知道查询什么表,所以返回结果使用一个泛型 T。
然后再来一个抽象类 AbstractDBHandler,把四个固定的步骤(1,2,3,5)在这个父类中实现,而第 4 步使用一个抽象方法交给子类去实现
public abstract class AbstractDBHandler<T> implements DBHandler<T> { protected abstract T getResult(ResultSet resultSet); @Override public List<T> quary(String sql, List<Object> params) { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { // 1.建立连接 connection = getConnection(); // 2.建立statement,并将数据注入预编译sql当中 statement = getStatement(connection, sql, params); // 3.执行查询,获取结果 resultSet = statement.executeQuery(); // 4.处理查询结果,将查询结果转换为对应的实体类对象 List<T> result = new ArrayList<>(); while (resultSet.next()) { T t = getResult(resultSet); result.add(t); } return result; } catch (SQLException e) { throw new IllegalStateException(e); } finally { // 5.关闭资源 release(resultSet, statement, connection); } } private Connection getConnection() { try { return DriverManager.getConnection(ConnectConsts.URL,ConnectConsts.USERNAME,ConnectConsts.PASSWORD); } catch (SQLException e) { throw new IllegalStateException(e); } } private PreparedStatement getStatement(Connection connection, String sql, List<Object> params) throws SQLException { PreparedStatement statement = connection.prepareStatement(sql); for (int i = 0; i < params.size(); i++) { statement.setObject(i + 1, params.get(i)); } return statement; } private void release(ResultSet resultSet, PreparedStatement statement, Connection connection) { try { if (null != resultSet) { resultSet.close(); } if (null != statement) { statement.close(); } if (null != connection) { connection.close(); } } catch (SQLException e) { throw new IllegalStateException(e); } } }
可以看到:第 4 步中只是调用了一下抽线方法 getResult(),具体的实现交给子类。
再来看一下两个子类:AccountDBHandler 和 UserDBHandler
public class AccountDBHandler extends AbstractDBHandler<Account> { @Override protected Account getResult(ResultSet resultSet) { try { return Account.builder() .id(resultSet.getInt("id")) .accountId(resultSet.getString("account_id")) .accountName(resultSet.getString("account_name")) .build(); } catch (SQLException e) { throw new IllegalStateException(e); } } }
public class UserDBHandler extends AbstractDBHandler<User> { @Override protected User getResult(ResultSet resultSet) { try { return User.builder() .id(resultSet.getLong("id")) .name(resultSet.getString("account_id")) .age(resultSet.getInt("account_name")) .build(); } catch (SQLException e) { throw new IllegalStateException(e); } } }
我们可以很清楚的看到,两个子类都是只实现了 getResult() 这个方法,不同的表获取不同的列,然后封装成不同的模板对象。
最后来看一下具体的方法调用,分别查询 account 表和 user 表
public class App { @Test public void test01(){ String sql = "select * from tb_account where id > ?"; ArrayList<Object> params = new ArrayList<>(); params.add(1); DBHandler<Account> accountDbHandler = new AccountDBHandler(); List<Account> accounts = accountDbHandler.quary(sql, params); accounts.forEach(System.out::println); } @Test public void test02(){ String sql = "select * from tb_user where id > ?"; ArrayList<Object> params = new ArrayList<>(); params.add(1); DBHandler<User> userDbHandler = new UserDBHandler(); List<User> accounts = userDbHandler.quary(sql, params); accounts.forEach(System.out::println); } }
我们只需要构造不同的 sql 和 params 来查询不同的表,使用表对应的实体类模板来封装查询结果。
AABCD
原文地址:https://www.cnblogs.com/L-Test/p/12990207.html
- Intellij idea 的maven项目自动下载jar包
- python3和python2共存
- 揭密微信跳一跳小游戏那些外挂
- 特斯拉出现人才流失潮,竟因为一些工程师认为Autopilot自动驾驶技术并不安全
- 微信又更新了,这次放出年度大招!新变化让不少人拍手叫好!
- “JINAN”:未来电动汽车边跑边充电
- Bagging算法
- 基于Region Proposal的深度学习目标检测简述(一)
- 10大数据挖掘算法及其简介
- SpringMVC返回图片的几种方式
- 区块链技术3.0来了,靠谱吗,看看区块链技术3.0能干啥
- SpringMVC支持跨域的几种姿势
- 万达回应网科裁员:系局部调整
- SpringMVC之请求参数的获取方式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- android自定义环形统计图动画
- 在Android环境下WebView中拦截所有请求并替换URL示例详解
- Android自定义控件横向柱状统计图
- Android处理视图圆角和色彩的工具类
- Flutter之Timer实现短信验证码获取60s倒计时功能的代码
- Android仿优酷视频的悬浮窗播放效果
- Android 本地广播和强制下线功能的实现代码
- Android实现强制下线功能的示例代码
- 如何用HMS Nearby Service给自己的App添加近距离数据传输功能
- Android自定义控件之圆形进度条动画
- Android 使用 Scroller 实现平滑滚动功能的示例代码
- Android文件操作工具类详解
- Android之RecycleView实现指定范围的拖动效果
- Android 通过代码安装 APK的方法详解
- Android ListView实现无限循环滚动