模板模式

  • 定义:定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现
  • 补充:模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤
  • 类型:行为型
  • 适用场景:
    • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
    • 各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复
  • 优点:
    • 提高复用性
    • 提高扩展性
    • 符合开闭原则
  • 缺点:
    • 类数目的增加
    • 增加了系统实现的复杂度
    • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍
  • 扩展:钩子方法,提供了缺省的行为,子类在必要时进行扩展
  • 相关设计模式:
    • 模板方法和工厂方法模式:工厂方法是模板方法的一种特殊实现
    • 模板方法和策略模式:策略模式目的是使不同的算法可以相互替换,并且不影响应用层客户端的使用,而模板方法模式是针对定义一个算法的流程,而将一些不太一样的实现步骤交给子类实现,模板方法不改变算法流程,策略模式可以改变算法流程

父类固定算法步骤

/**
 * <p>定义抽象算法类</p>
 *
 */
public abstract class AbstractCalculator {

    /**方法加final修饰下,防止子类恶意修改算法结构*/
    public final double calculate(String formula, String splitChar) {
        // 1、第一步,先根据计算公式和分隔符,拿到要计算的数值(假设这个是核心,不对外公布)
        double[] splits = split(formula, splitChar);
        // 2、第二步,各个算法类代表发个言吧(默认不发言的,一律按父类说的来)
        hookSay();
        // 3、第三步,话说完了,各个算法(子)类开始干活吧(这时候别再指望父类帮你默认实现了,休想!)
        return calculate(splits[0], splits[1]);
    }


    /**
     * <p>根据字符分割公式,提取double数组</p>
     *
     * @param formula   计算公式
     * @param splitChar 分隔字符
     * @return double数组
     */
    private double[] split(String formula, String splitChar) {
        String array[] = formula.trim().split(splitChar);
        double arrayDouble[] = new double[2];
        arrayDouble[0] = Double.parseDouble(array[0]);
        arrayDouble[1] = Double.parseDouble(array[1]);
        return arrayDouble;
    }

    /**
     * <p>公开(钩子)方法,子类和父类进行信息交互,子类可以使用默认的父类方法,也可以重写改方法</p>
     */
    public void hookSay() {
        System.out.println("父类说:算法步骤的顺序我已经定好了,尔等子类照着做就行!");
    }

    /**
     * <p>根据a和b的值计算结果,这个必须由子类来实现,父类帮不了了</p>
     *
     * @param numA 第一个数字
     * @param numB 第二个数字
     * @return 两个数字计算的结果
     */
    protected abstract double calculate(double numA, double numB);

}
/**
 * <p>加法运算</p>
 *
 */
public class Plus extends AbstractCalculator {

    @Override
    protected double calculate(double numA, double numB) {
        return numA + numB;
    }

    /**
     * 重写父类钩子方法
     */
    @Override
    public void hookSay() {
        System.out.println("子类Plus说:接下来,由我来计算结果!");
    }

}
/**
 * <p>减法运算</p>
 *
 */
public class Sub extends AbstractCalculator {
    @Override
    protected double calculate(double numA, double numB) {
        return numA - numB;
    }
}

 

UML

说明:类图关系很简单,就是一个抽象模板类,然后有两个实现类

JDBC模板方法实现

/**
 * <p>模拟JDB模板类(可以理解为伪代码说明)</p>
 *
 */
public abstract class AbstractJdbcTemplate {

    public final Object execute(String sql) throws SQLException {

        // 1、首先获取数据库连接对象
        Connection connection = getConnection();
        // 2、根据连接获取statement对象(数据库操作对象)
        getStament(connection);
        // 3、使用数据库操作对象来执行对应的SQL语句
        ResultSet rs = executeQuery(sql);
        // 4、最后基于查询的数据集,处理并返回最终结果
        return doInStatement(rs);

        // 资源的释放这里我就省略了
    }

    private Connection getConnection() {
        System.out.println("获取数据库连接");
        return null;
    }

    private Statement getStament(Connection connection) {
        System.out.println("获取数据库操作对象");
        return null;
    }

    private ResultSet executeQuery(String sql) {
        System.out.println("执行SQL语句:" + sql);
        return null;
    }

    /**
     * <p>基于结果集进行定制,由具体的子类实现</p>
     *
     * @param rs 结果集
     * @return 数据模型对象
     */
    protected abstract Object doInStatement(ResultSet rs) throws SQLException;

}
/**
 * <p>商品数据库操作(模板)实现类</p>
 *
 */
public class GoodsTemplateImpl extends AbstractJdbcTemplate {

    @Override
    protected Object doInStatement(ResultSet rs) throws SQLException {

        // 遍历数据集
//        while (rs.next()){
//           // dosomething
//        }

        System.out.println("数据集处理成功,返回Goods数据实体对象");
        return null;
    }

}
/**
 * <p>用户数据库操作(模板)实现类</p>
 *
 */
public class UserTemplateImpl extends AbstractJdbcTemplate {

    @Override
    protected Object doInStatement(ResultSet rs) throws SQLException {

        // 遍历数据集
//        while (rs.next()){
//           // dosomething
//        }

        System.out.println("数据集处理成功,返回User数据实体对象");
        return null;
    }
}

 

UML

JDBC模板和回调实现

/**
 * <p>数据库操作回调接口</p>
 *
 */
public interface IStatementCallback {
    Object doInStatement(Statement stmt) throws SQLException;
}
/**
 * <p>商品查询业务类</p>
 *
 */
public class GoodsQueryServiceImpl {

    public Object query(String sql) throws SQLException {
        return new JdbcTemplate().executeQuery(new GoodsQueryStatementCallBack(sql));
    }

}

/**
 * <p>内部类 == 用户查询数据库操作对象回调类</p>
 */
class GoodsQueryStatementCallBack implements IStatementCallback {

    private String sql;

    public GoodsQueryStatementCallBack(String sql) {
        this.sql = sql;
    }

    @Override
    public Object doInStatement(Statement stmt) throws SQLException {
        System.out.println("执行SQL语句:" + sql);
        System.out.println("数据集处理成功,返回Goods数据实体对象");
        return null;
    }

}
/**
 * <p>用户查询业务类</p>
 *
 */
public class UserQueryServiceImpl {

    public Object query(String sql) throws SQLException {
        return new JdbcTemplate().executeQuery(new UserQueryStatementCallBack(sql));
    }

}

/**
 * <p>内部类 == 用户查询数据库操作对象回调类</p>
 */
class UserQueryStatementCallBack implements IStatementCallback {

    private String sql;

    public UserQueryStatementCallBack(String sql) {
        this.sql = sql;
    }

    @Override
    public Object doInStatement(Statement stmt) throws SQLException {
        System.out.println("执行SQL语句:" + sql);
        System.out.println("数据集处理成功,返回User数据实体对象");
        return null;
    }

}
/**
 * <p>数据库操作模板类</p>
 *
 */
public class JdbcTemplate {

    public final Object execute(IStatementCallback callback) throws SQLException {

        // 1、首先获取数据库连接对象
        Connection connection = getConnection();
        // 2、根据连接获取statement对象(数据库操作对象)
        Statement stmt = getStament(connection);
        // 3、根据stmt回调子类方法 --> 执行sql语句获取最终结果
        return callback.doInStatement(stmt);

        // 资源的释放这里我就省略了
    }

    private Connection getConnection() {
        System.out.println("获取数据库连接");
        return null;
    }

    private Statement getStament(Connection connection) {
        System.out.println("获取数据库操作对象");
        return null;
    }

    public Object executeQuery(IStatementCallback callback) throws SQLException {
        return execute(callback);
    }

    /**
     * 如果该类是抽象类,则定义以下四个抽象方法,则其子类必须全部实现
     * 回调的好处,即子类只需要实现父类中的某一个方法即可,该方法可以抽离成一个接口的方法
     */
//    protected abstract void A();
//    protected abstract void B();
//    protected abstract void C();
//    protected abstract void D();
}

 

UML

测试

/**
 * <p>模板方法测试</p>
 *
 */
public class TemplateTest {

    public static void main(String[] args) throws SQLException {
        calculate();
        jdbc();
        jdbcCallBack();
    }

    private static void calculate() {

        System.out.println("====================== 11 + 2 加法公式计算 ");
        AbstractCalculator plus = new Plus();
        System.out.println(plus.calculate("11 + 2", "\\+"));

        System.out.println("====================== 11 - 2 减法公式计算");
        AbstractCalculator sub = new Sub();
        System.out.println(sub.calculate("11 - 2", "\\-"));

    }

    private static void jdbc() throws SQLException {

        System.out.println("====================== 用户数据层操作实现 ");
        AbstractJdbcTemplate userTemplate = new UserTemplateImpl();
        userTemplate.execute("select * from user ");
        System.out.println("====================== 商品数据层操作实现 ");
        AbstractJdbcTemplate goodsTemplate = new GoodsTemplateImpl();
        goodsTemplate.execute("select * from goods ");

    }

    private static void jdbcCallBack() throws SQLException {

        System.out.println("====================== 用户数据层操作实现(模板+回调) ");
        UserQueryServiceImpl queryService = new UserQueryServiceImpl();
        queryService.query("select * from user ");
        System.out.println("====================== 商品数据层操作实现(模板+回调)");
        GoodsQueryServiceImpl goodsQueryService = new GoodsQueryServiceImpl();
        goodsQueryService.query("select * from goods ");

    }

    /**
     *
     * 百科中如此描述模板方法:
     * 模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。
     * 让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。
     *
     * 模板方法和策略模式有点像,都是由子类最终实现算法,二者区别是:
     * (1)策略模式定义了一个统一的算法接口,由具体的算法子类实现,所有的算法方法必须暴露给子类,
     *      由子类来实现,这样会产生一个问题,就是在执行算法之前如果有大量的数据准备工作,则会
     *      增加子算法方法块代码的臃肿度,使得大量代码重复得不到复用!
     * (2)模板方法定一个抽象的类,由父类统一定义实现算法的步骤,具体实现延迟到子类,
     *      属于算法"定制",对特定的重复的算法部分实现了封装,只对扩展的部分暴露给子类!
     *
     *   优点:
     *   想必不用说,我们都可以看出来,模板方法的一个最大的好处就是实现了重复代码的复用!
     *   另外一个就是,扩展很方便!
     *   缺点:
     *   和策略模式一样,也存在子类过多的情况出现,类一多,维护起来的复杂度就增加了!
     *
     *   <p>假如,一个抽象类有4个子类,你可能会说,也就才4个啊,半个小时都能看的透透彻彻了</p>
     *   <p>假如,一个抽象类有100个子类,你可能会皱着眉头说,卧槽,这特么还是人吗,怎么写了这么多?</p>
     *   <p>所以,类并不是越多越能体现系统的健壮性,反而多了会适得其反噢!</p>
     */

}
=========执行结果============
====================== 11 + 2 加法公式计算 
子类Plus说:接下来,由我来计算结果!
13.0
====================== 11 - 2 减法公式计算
父类说:算法步骤的顺序我已经定好了,尔等子类照着做就行!
9.0
====================== 用户数据层操作实现 
获取数据库连接
获取数据库操作对象
执行SQL语句:select * from user 
数据集处理成功,返回User数据实体对象
====================== 商品数据层操作实现 
获取数据库连接
获取数据库操作对象
执行SQL语句:select * from goods 
数据集处理成功,返回Goods数据实体对象
====================== 用户数据层操作实现(模板+回调) 
获取数据库连接
获取数据库操作对象
执行SQL语句:select * from user 
数据集处理成功,返回User数据实体对象
====================== 商品数据层操作实现(模板+回调)
获取数据库连接
获取数据库操作对象
执行SQL语句:select * from goods 
数据集处理成功,返回Goods数据实体对象

 

源码中的应用

  • java.util.AbstractList:下面的子类实现ArrayList中有个方法,和它一样的抽象模板还有java.util.AbstractSetjava.util.AbstractMap

    

public boolean addAll(int index, Collection<? extends E> c) {
            rangeCheckForAdd(index);
            int cSize = c.size();
            if (cSize==0)
                return false;

            checkForComodification();
            parent.addAll(parentOffset + index, c);
            this.modCount = parent.modCount;
            this.size += cSize;
            return true;
        }

 

  •  javax.servlet.http.HttpServlet:对这个类应该很熟悉的,这个类定义了一套模板,doGet()doPost()doDelete()...,这也是模板方法在Servlet方法中的应用
posted @ 2024-01-22 15:45  wangzhilei  阅读(1)  评论(0编辑  收藏  举报