手写JdbcTemplate的模板方法模式

1,437 阅读5分钟

模板方法模式

模板方法模式(Template Method Pattern)又叫模板模式,是指定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤,属于行为型设计模式。

模板方法模式实际上是封装了一个固定流程,该流程由几个步骤组成,具体步骤可以由子类进行不同的实现,从而让固定的流程产生不同的结果。模板方法的本质就是抽象封装流程,然后进行类的继承实现。

通用UML类图

image.png

举例说明

我们平时在家自己做饭, (1)买菜,(2)洗菜, (3)做菜,(4)吃饭,(5)有时间的话洗碗,没时间的话就放到明天再洗,这五步就是定义的算法的流程;然后做不同菜需要买不同的材料,洗不同的材料,做法也不一样,这个就需要子类去具体实现。然后对于父类已经定义好的算法我们想做一些微调,那就是通过重写钩子方法

先定义一个做饭的算法,定义一个抽象类DodishTemplate,吃饭和洗碗没啥区别,已经实现好了,买菜,洗菜,做菜根据不同的菜做法是不一样的,需要子类自己去实现。第五步洗碗 haveTime 这个就是钩子方法,到底有没有时间由子类自己去考虑。

public abstract class DodishTemplate {
    /**
     * 做饭的过程
     */
    public final void dodish() {
        //1、买菜
        preparation();

        //2、洗菜
        washVegetables();

        //3、做菜
        doing();

        //4、吃饭
        eatting();

        //5、洗碗
        if (haveTime()) {
            washDishes();
        }
    }

    protected abstract void preparation();

    protected abstract void washVegetables();

    protected abstract void doing();

    protected void eatting() {
        System.out.println("吃饭");
    }

    //钩子方法
    protected abstract boolean haveTime();

    protected void washDishes() {
        System.out.println("洗碗");
    }

}

实现一个西红柿炒蛋类EggsWithTomato,买菜洗菜做菜,有没有时间子类都已经实现了,最后一个吃饭很喜欢吃,所以重写了父类的吃饭。

/**
 * 西红柿炒蛋
 */
public class EggsWithTomato extends DodishTemplate {

    @Override
    protected void preparation() {
        System.out.println("买西红柿和鸡蛋");
    }

    @Override
    protected void washVegetables() {
        System.out.println("洗西红柿和鸡蛋");
    }

    @Override
    protected void doing() {
        System.out.println("做西红柿炒蛋");
    }

    @Override
    protected boolean haveTime() {
        return false;
    }

    //重写
    @Override
    protected void eatting() {
        System.out.println("吃的很开心");
    }

}

实现一个红烧肉类RedBraisedMeat,买菜洗菜做菜,有没有时间子类都已经实现了

public class RedBraisedMeat extends DodishTemplate {
    @Override
    protected void preparation() {
        System.out.println("买肉");
    }

    @Override
    protected void washVegetables() {
        System.out.println("洗肉");
    }

    @Override
    protected void doing() {
        System.out.println("做红烧肉");
    }

    @Override
    protected boolean haveTime() {
        return true;
    }
}

最后写个测试类测试一下

public class Test {
    public static void main(String[] args) {
        System.out.println("=========做西红柿鸡蛋流程=========");
        EggsWithTomato eggsWithTomato = new EggsWithTomato();
        eggsWithTomato.dodish();

        System.out.println("=========做红烧肉流程=========");
        RedBraisedMeat redBraisedMeat = new RedBraisedMeat();
        redBraisedMeat.dodish();
    }
}

这个就是结果,在模板方法模式中,包含了抽象方法、具体方法和钩子方法,所以通过实现,重写,钩子可以达到差异化。 image.png

手写JDBC

创建一个模板类JdbcTemplate,封装所有的JDBC操作。以查询为例,每次查询的表不同,返回的数据结构也就不一样。我们针对不同的数据结构,都要封装成不同的实体对象,而每个实体封装的逻辑都是不一样的,但封装前和封装后的处理流程是不变的。因此,我们可以使用模板方法模式来设计这样的业务场景。

创建封装了所以处理流程的抽象类JdbcTemplate:

public abstract class JdbcTemplate {

    private DataSource dataSource;

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

    public final List<?> executeQuery(String sql, Object[] values) {
        try {
            //1、获取连接
            Connection conn = this.getConnection();
            //2、创建语句集
            PreparedStatement pre = this.createPrepareStatement(conn, sql);
            //3、执行语句集
            ResultSet rs = this.executeQuery(pre, values);
            //4、处理结果集
            List<Object> result = new ArrayList<>();
            while (rs.next()) {
                result.add(rowMapper(rs));
            }
            //5、关闭结果集
            rs.close();
            //6、关闭语句集
            pre.close();
            //7、关闭连接
            conn.close();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //实现
    protected abstract Object rowMapper(ResultSet rs) throws SQLException;

    //这个是不让重写的
    private ResultSet executeQuery(PreparedStatement pre, Object[] values) throws SQLException {
        for (int i = 0; i < values.length; i++) {
            pre.setObject(i, values[i]);
        }
        return pre.executeQuery();
    }

    //这个是不让重写的
    private PreparedStatement createPrepareStatement(Connection conn, String sql) throws SQLException {
        return conn.prepareStatement(sql);
    }

    //这个是不让重写的
    private Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }

}

创建实体对象Member类:

@Data
public class Member {

    private String username;
    private String password;
    private String nickname;
    private int age;
    private String addr;

}

创建数据库操作类MemberDao:

public class MemberDao extends JdbcTemplate {

    public MemberDao(DataSource dataSource) {
        super(dataSource);
    }

    public List<?> selectAll() {
        String sql = "select * from t_member";
        return super.executeQuery(sql, null);
    }

    @Override
    protected Object rowMapper(ResultSet rs) throws SQLException {
        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;
    }

}

客户端测试代码:

public class Test {
    public static void main(String[] args) {
        MemberDao memberDao = new MemberDao(null);
        List<?> result = memberDao.selectAll();
        System.out.println(result);
    }
}

优缺点

优点:

  1. 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
  2. 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
  3. 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则。

缺点:

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

本文及其他设计模式源码:github.com/xuhaoj/patt…

后记

在《论语·为政》中孔子说“吾十有五而志于学,三十而立,四十而不惑,五十而知天命,六十而耳顺,七十而从心所欲,不逾矩”。小伙们知道这句话运用的是哪个设计模式吗,这个其实就是本文讲的模板模式,我们可以把这句话理解为一套骨架算法,这个是改变不了的,我们每个人能做的就是实现这个接口或者抽象类,往自己的10-20岁,20-30岁,30-40岁。。。方法中间填充内容,而填充的丰富多彩,多姿多彩,还是平庸无奇,浑浑噩噩,全靠我们自己的想法。

小伙伴们,人的一生很短,十年也是一晃而过,我也是2010年上大学,到2020年四年大学,六年社会,仿佛就在昨天,2010年上海举办世博会,那时候我也到上海来参观,那时候浦东还是很破的,而现在浦东发展的今非昔比,十年足矣改变一个人。我也曾经爱玩过,我也曾经迷茫过,而现在我的眼里只是多了一份坚定,活到老学到老。现在我有能力了,希望通过写博客能够帮助到迷茫的人,种一棵树最好的时间是十年前,其次是现在,只要你行动了,任何时候都不晚。谢谢大家的观看~