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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM