模板模式(Template Pattern)

本文介绍了模板模式的概念,以及如何利用模板模式重构JDBC操作业务场景。通过具体的看病流程实例,解释了模板模式的适用场景和设计思想。同时,文章探讨了JDK中的AbstractList.get()方法作为模板模式的体现,并总结了模板模式的优缺点。

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

模板模式

首先来看一下模板模式的简介:模板模式(Template Pattern),一是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于行为性设计模式。模板方法适用于以下应用场景:

1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

2、各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

生活中的很多小事都是模板模式的体现,比如我们去医院看病的流程:1.挂号 2.看病

3.做检查 4.拿药,下面我们通过代码来体会一下。

首先写一个看病的抽象类SeeDoctor,特别提一下,在这个类中有一个钩子方法needChecked,关于钩子方法的定义:通过一个方法来干涉另一个方法的行为,就是一个方法的返回结果或修改的变量是另一个方法执行时的if判断条件或for/while循环调用条件的。在模板模式中,钩子方法和模板模式没有必然的关系,也就是说模板模式中可以有钩子方法也可以没有钩子方法,具体是否使用钩子方法根据业务判断。

/**
 * @author: Winston
 * @createTime: 2021/6/28
 * <p>
 * 模板会有一个或者多个未实现的方法
 * 而且这些未实现的方法有固定的执行顺序
 * 看病流程
 */
public abstract class SeeDoctor {

    /**
     * 去医院看病的流程
     */
    protected void goToHospital() {
        // 1.挂号
        this.register();
        // 2.找医生看病
        this.lookingForDoctor();
        // 3.做检查,有些病人是需要做检查的,而有些病人是不需要做检查的
        // 这里的needChecked()方法就是钩子方法
        if (needChecked()) {
            // 检查方法,病人做检查的项目不一样
            this.checked();
        }
        // 4.拿药
        this.takeMedicine();
    }

    public final void takeMedicine() {
        System.out.println("根据药方拿药");
    }

    protected abstract void checked();


    // 钩子方法,钩子方法的定义:通过一个方法来干涉另一个方法的行为,就是一个方法的返回结果或修改的变量是另一个方法执行时的if判断条件或for/while循环调用条件的
    protected boolean needChecked(){
        return false;
    }

    public final void lookingForDoctor() {
        System.out.println("找医生诊断");
    }

    public final void register() {
        System.out.println("挂号");
    }

}

编写张三看病类ZhangSanSeeDoctor

public class ZhangSanSeeDoctor extends SeeDoctor {

    @Override
    protected void checked() {
        System.out.println("张三做的检查项目:");
    }
}

编写李四看病类LiSiSeeDoctor

public class LiSiSeeDoctor extends SeeDoctor {

    /**
     * 是否需要检查
     */
    private boolean needChecked = false;

    /**
     * 通过构造方法将needChecked的值改变
     * @param needChecked
     */
    public LiSiSeeDoctor(boolean needChecked) {
        this.needChecked = needChecked;
    }

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

    @Override
    protected void checked() {
        System.out.println("李四做的检查项目:......");
    }
}

测试类

public class SeeDoctorTest {
    public static void main(String[] args) {
        System.out.println("================张三去医院看病========================");
        SeeDoctor patient1 = new ZhangSanSeeDoctor();
        patient1.goToHospital();

        System.out.println("================李四去医院看病========================");
        SeeDoctor patient2 = new LiSiSeeDoctor(true);
        patient2.goToHospital();
    }
}

运行结果:

================张三去医院看病========================
挂号
找医生诊断
根据药方拿药
================李四去医院看病========================
挂号
找医生诊断
李四做的检查项目:......
根据药方拿药

利用模板模式重构JDBC操作业务场景

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

先创建约束ORM逻辑的接口RowMapper

/**
 * @author: Winston
 * @createTime: 2021/6/28
 *
 * Spring中RowMapper接口的作用,用来把数据库中的列字段和java bean中属性对应上
 */
public interface RowMapper<T> {

    public T mapRow(ResultSet resultSet, int rowNumber);

}

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

/**
 * @author: Winston
 * @createTime: 2021/6/28
 */
public abstract class JdbcTemplate {

    /**
     * 数据源
     */
    private DataSource dataSource;

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

    public List<?> executeQuery(String sql, RowMapper rowMapper, Object... args) throws Exception {
        // 1.获取连接
        Connection connection = this.getConnection();
        // 2.创建语句集
        PreparedStatement preparedStatement = this.createPrepareStatement(connection, sql);
        // 3.执行语句集
        ResultSet resultSet = this.executeQuery(preparedStatement, args);
        // 4.处理结果集
        List<?> result = this.parseResultSet(resultSet, rowMapper);
        // 5.关闭结果集
        this.closeResult(resultSet);
        // 6.关闭语句集
        this.closePreparedStatement(preparedStatement);
        // 7.关闭连接
        this.closeConnection(connection);

        return result;
    }

    protected void closeConnection(Connection connection) throws SQLException {
        connection.close();
    }

    protected void closePreparedStatement(PreparedStatement preparedStatement) throws SQLException {
        preparedStatement.close();
    }

    protected void closeResult(ResultSet resultSet) throws SQLException {
        resultSet.close();
    }

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

    protected ResultSet executeQuery(PreparedStatement preparedStatement, Object[] args) throws SQLException {
        for (int i = 0; i < args.length; i++) {
            preparedStatement.setObject(i, args[i]);
        }

        return preparedStatement.executeQuery();
    }


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

    protected Connection getConnection() throws SQLException {
        return this.dataSource.getConnection();
    }
}

创建实体类User

public class User {
    private String username;
    private String password;
    private String mobile;
    private Integer age;

    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 getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

创建数据库操作类UserDao

/**
 * @author: Winston
 * @createTime: 2021/6/28
 */
public class UserDao extends JdbcTemplate{

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


    public List<?> selectAll() throws Exception {
        String sql = "SELECT * FROM t_user";

        return super.executeQuery(sql, new RowMapper() {
            @Override
            public Object mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
                // 将实体类和数据库字段对应上
                String username = resultSet.getString("username");
                String password = resultSet.getString("password");
                String mobile = resultSet.getString("mobile");
                Integer age = resultSet.getInt("age");

                User user = new User();
                user.setUsername(username);
                user.setPassword(password);
                user.setMobile(mobile);
                user.setAge(age);

                return user;
            }
        }, null);
    }
}


测试类,这里由于没有配置Spring环境和数据库环境,仅做模拟。

public class UserTest {
    public static void main(String[] args) {
        DataSource dataSource = null;
        UserDao userDao = new UserDao(dataSource);
        try {
            List<?> result = userDao.selectAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

模板模式在JDK源码中的体现

先来看看JDK中AbstractList的get()方法,可以看到get()是一个抽象方法,我们经常用的ArrayList就是AbstractList的子类,由于AbstractList不同的子类所实现的get()方法有所不同,所以这也是模板模式的一种体现。

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    ... 
	abstract public E get(int index);
    ...
}

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	...
	 public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
	...
}

模板模式小结

模板模式就好比是造房子,大致的步骤是规定好的,比如先打地基、然后买水泥黄沙等,至于房子盖的形状可以不同。

模板模式的优缺点

优点:

1、利用模板方法将相同处理逻辑的代码放到抽象类中,可以提高代码的复用性。

2、将不同的代码在不同的子类中实现,通过对子类的扩展增加新的行为,提高代码的扩展性。

3、把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则。

缺点:

1、类数目的增加,每一个抽象类都需要至少一个子类来实现,这样导致类的个数增加,从而导致系统复杂度的增加。

2、继承关系自身的缺点,如果父类添加了新的抽象方法,所有子类都要增加该方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值