JAVA设计模式之-模板方法+(钩子函数)

1.定义

   允许子类对父类的一个或多个步骤进行重写。例如聚合支付场景中有很多共同的步骤,比如验签、四要素验证、风控等等,但是在支付的时候走不同的渠道可能在调用和参数上有很大的不同,比如有的是xml,有的是json,等等。 我们就可以用父类实现通用的逻辑,由子类实现不同的交互逻辑。

2.模板方法+钩子函数示例

顶层接口 BasePay

public interface BasePay {
    //移动支付
    void mobilePay();
}

抽象类 AbstractBasePay

public abstract class AbstractBasePay implements BasePay {

    @Override
    public final void mobilePay() {
        // 钩子函数
        if (isCheckAuth()) {
            checkAuth();
        }
        checkParam();
        chenckRisk();
        channlPay();
    }

    private void checkParam() {
        System.out.println("检查参数");
    }
    
    private void checkAuth() {
        System.out.println("支付权限校验");
    }
    
    private void chenckRisk() {
        System.out.println("风控校验");
    }
    
    //渠道支付
    abstract void channlPay();
    
    //钩子函数,子类可以覆写,来选择手开启支付权限校验  默认不开启
    boolean isCheckAuth() {
        return true;
    }
}

具体实现 1   中金支付

public class CPCNchannelPay extends AbstractBasePay{

    @Override
    void channlPay() {
        System.out.println("中金支付");
    }
    
    boolean isCheckAuth() {
        return false;
    }
}

具体实现2 阿里支付

public class AliChannelPay extends AbstractBasePay{

    @Override
    void channlPay() {
        System.out.println("阿里pay");
    }

}

运行及结果

public class TestPay {
    public static void main(String[] args) {
        System.out.println("--中金支付start");    
        BasePay pay1 = new CPCNchannelPay();
        pay1.mobilePay();
        System.out.println("--中金支付end");    

        
        System.out.println("--阿里paystart");    
        BasePay pay2 = new AliChannelPay();
        pay2.mobilePay();
        System.out.println("--阿里payend");    

    }
}

结果:

--中金支付start
检查参数
风控校验
中金支付
--中金支付end

--阿里paystart
支付权限校验
检查参数
风控校验
阿里pay
--阿里payend

 我们可以看到中金支付中,我们把钩子返回设置成了false ,就没有进行支付权限校验

4.模板方法在框架源码中的应用

  比如mybatis 对于入参设值,和返回参数映射成实体类时的类型转换,就是用的模板方法。

  类图如下:

  说明:

  ① TypeHandler接口

  这个接口有三个方法,一个set,用来给PreparedStatement对象对应的列设置参数;两个get,从ResultSet和CallableStatement获取对应列的值,不同之处是一个是取第几个位置的值,一个是取具体列名所对应的值。set用来将Java对象中的数据类型转换为JDBC中对应的数据类型,get用来将JDBC中对应的数据类型转换为Java对象中的数据类型转换。

  

  ②BaseTypeHandler抽象类

在进行软件设计时提倡面向接口的设计,但接口只是一个接口,并不做任何实质性的操作,还需有一系列的实现才可以真正的达到目标。BaseTypeHandler类便是对TypeHandler接口的初步实现,在实现TypeHandler接口的三个函数外,又引入了3个抽象函数用于null值的处理。

  

  ④DateTypeHandler

书接上文,BaseTypeHandler类也是一个抽象类,按照Java的规定抽象类并不能初始化,也不能直接使用,因而还需要有具体的类。在type包中有十多个具体的类来具体处理类型转换,每一个类处理一个数据类型,像long、int、double等等,我们以一个稍微复杂些的DateTypeHandler类为例,了解下对日期是如何进行处理的。

  

  1)setNonNullParameter

public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
      throws SQLException { ps.setTimestamp(i, new java.sql.Timestamp(((Date) parameter).getTime())); }

首先将参数parameter这个Object转换为Date类型,而后通过Date对象的getTime()将日期转为毫秒数,而后再将毫秒数转换为java.sql.Timestamp对象。即将java.util.Date对象转换为java.sql.Timestamp对象。

  2)getNullableResult

复制代码
public Object getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    java.sql.Timestamp sqlTimestamp = rs.getTimestamp(columnName);
    if (sqlTimestamp != null) { return new java.util.Date(sqlTimestamp.getTime()); } return null; } public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { java.sql.Timestamp sqlTimestamp = cs.getTimestamp(columnIndex); if (sqlTimestamp != null) { return new java.util.Date(sqlTimestamp.getTime()); } return null; }
复制代码

从上面的代码可以看出这两个函数的作用就是将 java.sql.Timestamp对象转换为 java.util.Date对象.

1)setNonNullParameter

public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
      throws SQLException { ps.setTimestamp(i, new java.sql.Timestamp(((Date) parameter).getTime())); }

首先将参数parameter这个Object转换为Date类型,而后通过Date对象的getTime()将日期转为毫秒数,而后再将毫秒数转换为java.sql.Timestamp对象。即将java.util.Date对象转换为java.sql.Timestamp对象。

2)getNullableResult

复制代码
public Object getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    java.sql.Timestamp sqlTimestamp = rs.getTimestamp(columnName);
    if (sqlTimestamp != null) { return new java.util.Date(sqlTimestamp.getTime()); } return null; } public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { java.sql.Timestamp sqlTimestamp = cs.getTimestamp(columnIndex); if (sqlTimestamp != null) { return new java.util.Date(sqlTimestamp.getTime()); } return null; }
复制代码

从上面的代码可以看出这两个函数的作用就是将 java.sql.Timestamp对象转换为 java.util.Date对象.

 参考博客:https://www.cnblogs.com/sunzhenchao/archive/2013/04/09/3009431.html

猜你喜欢

转载自www.cnblogs.com/chenfei-java/p/12500967.html