设计模式实战之模板模式
发布日期:2021-05-08 05:04:41 浏览次数:19 分类:原创文章

本文共 9507 字,大约阅读时间需要 31 分钟。

写在前面

上一篇讲了策略模式,知道了可以使用策略模式对多重if-else进行优化,而且符合开闭原则。那么除了策略模式,还有什么设计模式比较好用而且常用的呢。这就是今天要讲的模板模式。

模板模式解决什么问题呢?

正片开始

首先我们使用SpringBoot来搭建一个工程。

	<!-- maven配置 -->	<dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>        <groupId>commons-lang</groupId>        <artifactId>commons-lang</artifactId>        <version>2.6</version>    </dependency>    <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>        <scope>runtime</scope>    </dependency>

application.yml配置如下:

server:  port: 8888spring:  datasource:    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8    username: 账号    password: 密码    driver-class-name: com.mysql.jdbc.Driver

创建一个全局配置类GlobalProperties,我们通过这个类可以获取yml的配置信息

@Component("globalProperties")public class GlobalProperties {       @Value("${spring.datasource.driver-class-name}")    private String driverClass;    @Value("${spring.datasource.url}")    private String url;    @Value("${spring.datasource.username}")    private String username;    @Value("${spring.datasource.password}")    private String password;        //字段对应的getter、setter方法...

创建一个连接工厂类ConnectFactory,获取数据库连接

public class ConnectFactory {       public static Connection getConnection() throws Exception{           //获取yml文件的配置,SpringContextUtil工具类在上一篇文章有介绍,可以参考上一篇文章的代码        GlobalProperties properties = SpringContextUtil                .getBean("globalProperties", GlobalProperties.class);        //加载数据驱动        Class.forName(properties.getDriverClass());        //获取数据库连接,返回数据库连接对象        return DriverManager.getConnection(properties.getUrl(),                properties.getUsername(), properties.getPassword());    }}

创建实体类User

public class User {       private Integer id;    private String name;    private Integer age;    private String job;        //字段对应的getter、setter方法...

接着在mysql对应的数据库创建数据表tb_user,sql语句如下:

CREATE TABLE `tb_user` (  `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '主键',  `name` varchar(255) NOT NULL COMMENT '名称',  `age` tinyint(4) NOT NULL COMMENT '年龄',  `job` varchar(255) DEFAULT NULL COMMENT '工作',  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

插入一些测试数据

INSERT INTO tb_user(`name`,`age`,`job`) VALUES('大司马',36,'厨师');INSERT INTO tb_user(`name`,`age`,`job`) VALUES('朴老师',36,'主播');INSERT INTO tb_user(`name`,`age`,`job`) VALUES('王刚',30,'厨师');INSERT INTO tb_user(`name`,`age`,`job`) VALUES('大sao',32,'美食up主');INSERT INTO tb_user(`name`,`age`,`job`) VALUES('姚大秋',35,'主持人');

假设我们有一张user表,我们通过原生的JDBC来进行数据库操作,那么需要在dao层完成以下几步。

1.装载相应的数据库的JDBC驱动并进行初始化

2.建立JDBC和数据库之间的Connection连接

3.创建Statement或者PreparedStatement接口,执行SQL语句

4.处理和显示结果

5.释放资源

例子如下:

	private static final String GET_USER_BY_NAME_SQL = "SELECT `id`,`name`,`age`,`job` FROM `tb_user` WHERE `name` = '%s'";		@Override    public User getUserByName(String name) throws Exception {           User user = new User();        //获取数据连接        try (Connection connection = ConnectFactory.getConnection();             Statement statement = connection.createStatement();             ResultSet resultSet = statement             .executeQuery(String.format(GET_USER_BY_NAME_SQL, name))        ) {               while (resultSet.next()) {                   //获取id                user.setId(resultSet.getInt("id"));                //获取名称                user.setName(resultSet.getString("name"));                //获取年龄                user.setAge(resultSet.getInt("age"));                //获取工作                user.setJob(resultSet.getString("job"));            }        } catch (Exception e) {               e.printStackTrace();        }        return user;    }	private static final String GET_USER_BY_ID_SQL = "SELECT `id`,`name`,`age`,`job` FROM `tb_user` WHERE `id` = '%s'";    @Override    public User getUserById(Integer id) throws Exception {           User user = new User();        //获取数据库连接        try (Connection connection = ConnectFactory.getConnection();             Statement statement = connection.createStatement();             ResultSet resultSet =             statement.executeQuery(String.format(GET_USER_BY_ID_SQL, id))        ) {               while (resultSet.next()) {                   //获取id                user.setId(resultSet.getInt("id"));                //获取名称                user.setName(resultSet.getString("name"));                //获取年龄                user.setAge(resultSet.getInt("age"));                //获取工作                user.setJob(resultSet.getString("job"));            }        } catch (Exception e) {               e.printStackTrace();        }        return user;    }

PS:这里为了简单一点就直接把参数拼接sql语句,不采用预编译来处理sql的参数。

问题分析

通过上面的代码,我们很明显可以看到是有很大的问题的。

1.每次在进行数据库操作都需要获取Connection对象,创建Statement对象。

2.每次获取结果后,都要进行结果处理,而且如果是同一张表的查询,会很重复。每次都需要把结果值set回到对象的字段中。

模板模式就可以解决这个问题!

使用模板模式重构代码

第一步

创建一个模板类DaoTemplate,如下:

/** * @author Ye Hongzhi * @program DaoTemplate * @description * @date 2020-04-12 17:04 **/@Componentpublic class DaoTemplate {       public <T> T query(String sql, Class<T> clazz) throws Exception {           //通过clazz创建返回值对象        T t = clazz.newInstance();        //获取数据库连接        try (Connection connection = ConnectFactory.getConnection();             Statement statement = connection.createStatement();             ResultSet resultSet = statement.executeQuery(sql)        ) {               while (resultSet.next()) {                   //获取所有字段                Field[] fields = clazz.getDeclaredFields();                //获取所有方法                Method[] methods = clazz.getDeclaredMethods();                Map<String, Method> methodNameMap = Arrays.stream(methods)                        .collect(Collectors.toMap(Method::getName, Function.identity()));                //把数据库对应的列的值赋值给 泛型T对象的 对应的字段                for (Field field : fields) {                       //获取字段名                    String fieldName = field.getName();                    //获取set方法                    Method method = methodNameMap.get("set" + change(fieldName));                    //获取数据库的列的值                    Object fieldValue = null;                    if (field.getType() == String.class) {                           fieldValue = resultSet.getString(fieldName);                    }                    if (field.getType() == Integer.class) {                           fieldValue = resultSet.getInt(fieldName);                    }                    if (field.getType() == Boolean.class) {                           fieldValue = resultSet.getBoolean(fieldName);                    }                    if (field.getType() == Long.class) {                           fieldValue = resultSet.getLong(fieldName);                    }                    if(field.getType() == Double.class){                           fieldValue = resultSet.getDouble(fieldName);                    }                    if(field.getType() == BigDecimal.class){                           fieldValue = resultSet.getBigDecimal(fieldName);                    }                    if (field.getType() == Date.class) {                           fieldValue = resultSet.getDate(fieldName);                    }                    //设置更多的字段类型...                    //利用反射执行对象的set方法,把数据库的值设置到对象的字段中                    method.invoke(t, fieldValue);                }            }        } catch (Exception e) {               e.printStackTrace();        }        return t;    }    /**     * 将一个字符串首字母大写,其它字母小写     *     * @param str 字符串     * @return     */    private static String change(String str) {           return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();    }}

第二步

创建模板后,可以在DAO层引入模板,然后使用。如下:

	//引入模板		@Resource    private DaoTemplate daoTemplate;	private static final String GET_USER_BY_ID_SQL = "SELECT `id`,`name`,`age`,`job` FROM `tb_user` WHERE `id` = '%s'";    @Override    public User getUserById(Integer id) throws Exception {           //使用模板的方法,查询        return daoTemplate.query(String.format(GET_USER_BY_ID_SQL, id), User.class);    }	private static final String GET_USER_BY_NAME_SQL = "SELECT `id`,`name`,`age`,`job` FROM `tb_user` WHERE `name` = '%s'";    @Override    public User getUserByName(String name) throws Exception {           //使用模板的方法,查询        return daoTemplate.query(String.format(GET_USER_BY_NAME_SQL, name), User.class);    }

哇喔!突然间代码就显得清爽很多了!

小伙伴们看到这里,get到新的技能了吗?

扩展知识

实际上在Spring框架就有提供JDBC模板

我们可以在MAVEN中引入以下配置:

		<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-jdbc</artifactId>        </dependency>

创建一个映射类UserRowMapper

public class UserRowMapper implements RowMapper<User> {       @Override    public User mapRow(ResultSet resultSet, int i) throws SQLException {           User user = new User();        user.setId(resultSet.getInt("id"));        user.setName(resultSet.getString("name"));        user.setAge(resultSet.getInt("age"));        user.setJob(resultSet.getString("job"));        return user;    }}

然后在DAO层的UserDaoImpl,我们就可以引入JdbcTemplate

	@Resource    private JdbcTemplate jdbcTemplate;		//使用jdbcTemplate查询	@Override    public User getUserByName(String name) throws Exception {           return jdbcTemplate.queryForObject(String.format(GET_USER_BY_NAME_SQL, name), new UserRowMapper());    }

从这里可以看出实际上Spring框架就是采用这种思想来实现JdbcTemplate模板。

结束语

所以在实际项目的开发中,我们有时候遇到某些代码块的前后都有重复操作时,可以采用模板模式去重构代码,使代码更加简洁,容易维护。

想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!
在这里插入图片描述

能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

上一篇:设计模式之代理模式以及应用
下一篇:IDEA出现错误:找不到或无法加载主类 io.xxx.XXXApplication

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年03月27日 20时18分07秒