模板模式(spring JdbcTemplate 实际案例)

本文详细介绍了设计模式中的模板方法模式,通过实例展示了如何在Java中使用模板模式来规范流程并实现代码复用。文章讨论了模板模式的六大原则,并以数据库操作为例,解释了JdbcTemplate如何利用模板模式简化数据库查询。此外,还探讨了模板模式与依赖倒置原则的关系,强调了代码灵活性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式原则

  • 开闭原则 对扩展开放,对修改关闭。
  • 依赖倒置原则 通过抽象使各个类或者模块不相互影响,实现松耦合。
  • 单一职责原则一个类、接口、方法只做一件事。 接口隔离原则 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。
  • 迪米特法则又叫最少知道原则,一个类对其所依赖的类知道得越少越好。 里氏替换原则 子类可以扩展父类的功能但不能改变父类原有的功能。
  • 合成复用原则尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。

模板模式

我们平时办理入职流程填写入职登记表–>打印简历–>复印学历–>复印身份证–>签订 劳动合同–>建立花名册–>办理工牌–>安排工位等;再比如,我平时在家里炒菜:洗锅 -->点火–>热锅–>上油–>下原料–>翻炒–>放调料–>出锅;再比如赵本山问宋丹丹: “如何把大象放进冰箱?”宋丹丹回答:“第一步:打开冰箱门,第二步:把大象塞进 冰箱,第三步:关闭冰箱门”。赵本山再问:“怎么把长劲鹿放进冰箱?”宋丹丹答: “第一步:打开冰箱门,第二步:把大象拿出来,第三步:把长劲鹿塞进去,第四步: 关闭冰箱门”,这些都是模板模式的体现。

模板模式通常又叫模板方法模式(Template Method Pattern)是指定义一个算法的骨 架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结 构的情况下,重新定义算法的某些步骤,属于行为性设计模式。模板方法适用于以下应 用场景:
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

例子:发布预习资料–>制作课件 PPT–>在线直播 --> 提 交 课 堂 笔 记 --> 提 交 源 码 --> 布 置 作 业 --> 检 查 作 业 。

模板类 上课流程模板
/**
 * @Author jyl
 * @Date 2021/12/14 14:00
 * @Version 1.0
 */
/**
 * 模板会有一个或者多个未现实方法,
 * 而且这几个未实现方法有固定的执行循序
 *
 */
public abstract class NetworkCourse {

    public final void createCourse(){
        //1、发布预习资料
        this.postPreResource();

        //2、制作PPT课件
        this.createPPT();

        //3、在线直播
        this.liveVideo();

        //4、提交课件、课堂笔记
        this.postNote();

        //5、提交源码
        this.postSource();

        //6、布置作业,有些课是没有作业,有些课是有作业的
        //如果有作业的话,检查作业,如果没作业,完成了
        //这里是一个钩子函数用来微调流程
        if(needHomework()){
            checkHomework();
        }
    }

    protected abstract void checkHomework();

    //钩子方法:实现流程的微调
    protected boolean needHomework(){return false;}

    final void postSource(){
        System.out.println("提交源代码");
    }

    final void postNote(){
        System.out.println("提交课件和笔记");
    }

    final void liveVideo(){
        System.out.println("直播授课");
    }

    final void createPPT(){
        System.out.println("创建备课PPT");
    }

    final void postPreResource(){
        System.out.println("分发预习资料");
    }

}
上课流程模板实现类

java课程流程

/**
 * @Author jyl
 * @Date 2021/12/14 14:04
 * @Version 1.0
 */
public class JavaCourse extends NetworkCourse {
    public void checkHomework() {
        System.out.println("检查Java的架构课件");
    }
}

大数据课程流程

/**
 * @Author jyl
 * @Date 2021/12/14 14:05
 * @Version 1.0
 */
public  class BigDataCourse extends NetworkCourse {

    private boolean needHomeworkFlag = false;

    public BigDataCourse(boolean needHomeworkFlag) {
        this.needHomeworkFlag = needHomeworkFlag;
    }

    public void checkHomework() {
        System.out.println("检查大数据的课后作业");
    }

    @Override
    protected boolean needHomework() {
        return this.needHomeworkFlag;
    }
}

客户端调用
/**
 * @Author jyl
 * @Date 2021/12/14 14:08
 * @Version 1.0
 */
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();

    }
}

模板模式JdbcTemplate 的实际案例

原始连接类

首先是先是pojo类
public class Member {

    private String username;
    private String password;
    private String nickname;
    private int age;
    private String addr;
//setget省略
}

然后是我们原始查询类
/**
 1. @Author jyl
 2. @Date 2021/12/14 14:08
 3. @Version 1.0
 */
public class JdbcDome {

    private  DataSource dataSource;

    public JdbcDome(DataSource dataSource ){
        this.dataSource = dataSource;
    }

    public List<Member> executeQuery(String sql, Object[] values){
        try {

            /*1创建链接*/
            Connection connection = dataSource.getConnection();

            /*2创建语句集*/
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < values.length; i++) {
                preparedStatement.setObject(i,values[i]);
            }
            /*3执行语句集*/
            ResultSet resultSet = preparedStatement.executeQuery();

            /*4处理结果*/
            List<Member> result = new ArrayList<Member>();
            int rowNum = 1;
            while (resultSet.next()){
                Member member = new Member();
                //字段过多,原型模式
                member.setUsername(resultSet.getString("username"));
                member.setPassword(resultSet.getString("password"));
                member.setAge(resultSet.getInt("age"));
                member.setAddr(resultSet.getString("addr"));
                result.add(member);

            }

            /*5关闭链接*/
            connection.close();
            /*6关闭语句集*/
            preparedStatement.close();
            /*7关闭结果集*/
            resultSet.close();

            return result;


        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

看完我们的查询类发现 好像使用jdbc查询 可以总结出来步骤 一共是7步骤

  1. 创建链接
  2. 创建语句集
  3. 执行语句集
  4. 处理结果
  5. 关闭链接
  6. 关闭语句集
  7. 关闭结果集
    嗯~每次实现一次数据库操作都这样 这就像一种固定的流程 像我们上课的案例 在发生改变 如果我们有很多个pojo 比如 person 还能有 dog cat 等等 我们都要执行 相同的步骤。我们完全可以不这么勤快 来把它们公共的部分抽取成一个父类 来存放那些公共的操作 如果需要改变那个步骤的具体实现我们只需要 重写父类方法就行了至于那些 不确定的 只需要重写父类方法就行了 其实在这里有个疑惑 我们为啥不直接 不抽象这个方法 只用子类实现就好了 这里得回去看看 我们设计模式的依赖倒置原则了我们不能依赖细节我们一定要依赖抽象来保证我们代码的灵活性 比如 我们经常使用 List list = new ArrayList();如果我们list 不包含ArrayList的一个方法的话 在引用的时候是不是会出现一个文件 也就是 我们 使用 父类接受子类的使用 方法会遗失 那么我们必要的流程就少了一个 就像是 洗锅 -->点火–>热锅–>上油–>下原料–>翻炒–>放调料–>出锅 变成了

洗锅 -->点火–>热锅–>上油–>缺失的–>翻炒–>放调料–>出锅
模板模式的适用场景就是 流程确定将必须的公共的提取到父类 避免代码重复

抽象后的模板类

提取了公共部分 并且把公共部分封装为一个流程

模板类
/**
 * @Author jyl
 * @Date 2021/12/14 13:24
 * @Version 1.0
 */
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 conn = this.getConnection();
            //2、创建语句集
            PreparedStatement pstm = this.createPrepareStatement(conn,sql);
            //3、执行语句集
            ResultSet rs = this.executeQuery(pstm,values);
            //4、处理结果集
            List<?> result = this.paresResultSet(rs,rowMapper);
            //5、关闭结果集
            this.closeResultSet(rs);
            //6、关闭语句集
            this.closeStatement(pstm);
            //7、关闭连接
            this.closeConnection(conn);
            return result;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    protected void closeConnection(Connection conn) throws Exception {
        //数据库连接池,我们不是关闭
        conn.close();
    }

    protected void closeStatement(PreparedStatement pstm) throws Exception {
        pstm.close();
    }

    protected void closeResultSet(ResultSet rs) throws Exception {
        rs.close();
    }

    protected List<?> paresResultSet(ResultSet rs, RowMapper<?> rowMapper) throws Exception {
        List<Object> result = new ArrayList<Object>();
        int rowNum = 1;
        while (rs.next()){
            result.add(rowMapper.mapRow(rs,rowNum ++));
        }
        return result;
    }

    protected ResultSet executeQuery(PreparedStatement pstm, Object[] values) throws Exception {
        if (values==null){
            return pstm.executeQuery();
        }
        for (int i = 0; i < values.length; i++) {
            pstm.setObject(i,values[i]);
        }
        return pstm.executeQuery();
    }

    protected PreparedStatement createPrepareStatement(Connection conn, String sql) throws Exception {
        return conn.prepareStatement(sql);
    }

    public Connection getConnection() throws Exception {
        return this.dataSource.getConnection();
    }
}
ORM映射定制化的接口

这个接口 是为了不同的 pojo的类来返回不同的pojo的结果 十分灵活的利用接口

/**
 * ORM映射定制化的接口
 * 
 */
public interface RowMapper<T> {
    T mapRow(ResultSet rs, int rowNum) throws Exception;
}

模板实现类
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>() {
            public Member mapRow(ResultSet rs, int rowNum) throws Exception {
                Member member = new Member();
                //字段过多,原型模式
                member.setUsername(rs.getString("username"));
                member.setPassword(rs.getString("password"));
                member.setAge(rs.getInt("age"));
                member.setAddr(rs.getString("addr"));
                return member;
            }
        },null);
    }
}

客户端测试类
/**
 * @Author jyl
 * @Date 2021/12/14 16:02
 * @Version 1.0
 */
public class MemberDaoTest {


    public static void main(String[] args) throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        String url =  "jdbc:mysql://localhost/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
        druidDataSource.setUrl(url);
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");
        druidDataSource.getConnection();
        MemberDao memberDao = new MemberDao(druidDataSource);
        List<Member> result = (List<Member>) memberDao.selectAll();
        for (Member n: result) {
             System.out.println(n.getUsername());
        }
    }
}

上面的就是我们JdbcTemplate 的案例 可以看得到模板模式规定了必须有的流程 在我看来有时候也并非是必须要流程 而是一种必须拥有的方法 比如

AbstractList

在这里插入图片描述

ArrayQueue

在这里插入图片描述#### LinkedList
在这里插入图片描述
其实这里就只是抽取了公共的方法 并没有什么流程 ,在我看来模板方法其实也不用太拘泥于流程 而是公用的方法 能统一到父类 的方法 使得大部分子类可以避免写重复的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值