摘要:模板方法模式定義了一個算法的步驟,并允許子類別為一個或多個步驟提供其實踐方式。在軟件工程中,它是一種軟件設計模式,和模板沒有關連。模板方法充分運用了多態與繼承。去建設銀行支付去招商銀行支付實現模板方法的細節,我們來看使用邏輯。
Photo by Tomá? Malík on Unsplash
什么是模板方法模式?摘錄 wiki 的介紹。
模板方法模式定義了一個算法的步驟,并允許子類別為一個或多個步驟提供其實踐方式。讓子類別在不改變算法架構的情況下,重新定義算法中的某些步驟。在軟件工程中,它是一種軟件設計模式,和C++模板沒有關連。
模板設計方法存在目的在于某些算法邏輯存在一些相同處,而具體細節卻不同。這樣使用模板方法,可以抽取共用邏輯到父類,在子類實現具體算法細節,這樣減少了重復代碼。
模板方法充分運用了多態與繼承。使用抽象父類定義抽象操作,然后在公共邏輯調用抽象方法。子類方法只要繼承父類關注自身實現細節。
Talk is cheap. Show me the code
下面拿支付接入支付渠道例子來使用模板方法。
假設銀行卡支付需要實現兩家銀行的支付功能。不同銀行提供的接口,在參數,調用方式等肯定存在很大區別。這個時候我們就可以使用模板設計方法,父類實現支付前通用邏輯,用子類實現交互的不同。系統類結構如下。
AgreementPay 提供支付功能,AgreementBasePay 為抽象類實現通用邏輯,AgreementCCBPay 與 AgreementCMBPay 實現具體的渠道支付方法。具體源碼如下。
AgreementPay 接口
public interface AgreementPay { PayResponse payInChannel(PayRequest request); }
AgreementBasePay 抽象方法實現通用邏輯。
public abstract class AgreementBasePay implements AgreementPay { public PayResponse pay(PayRequest request) { checkRequest(request); return this.payInChannel(request); } private void checkRequest(PayRequest request) { System.out.println("具體方法參數檢查"); } }
具體實現類,實現具體渠道支付細節。
public class AgreementCCBPay extends AgreementBasePay { @Override public PayResponse payInChannel(PayRequest request) { System.out.println("去建設銀行支付"); return new PayResponse(); } } public class AgreementCMBPay extends AgreementBasePay { @Override public PayResponse payInChannel(PayRequest request) { System.out.println("去招商銀行支付"); return new PayResponse(); } }
實現模板方法的細節,我們來看 client 使用邏輯。
public class Client { public static void main(String[] args) { System.out.println("使用招商銀行支付"); AgreementPay agreementPay = new AgreementCMBPay(); PayRequest request = new PayRequest(); agreementPay.payInChannel(request); System.out.println("使用建設銀行支付"); agreementPay = new AgreementCCBPay(); agreementPay.payInChannel(request); } }
上面 client 邏輯,其實看起來還是有一些死板,且需要外部知道調用哪個渠道接口。但是如果真正提供一個對外接口,外部調用方法是不關心你具體使用那個子類支付。所以這里我們可以改進一下,
public static MappayCache = new HashMap<>(); static { payCache.put("CMB", new AgreementCMBPay()); payCache.put("CCB", new AgreementCCBPay()); } public static void main(String[] args) { PayRequest request = new PayRequest(); AgreementPay pa; switch (request.getBankCode()) { case "CMB": pa = payCache.get("CMB"); pa.payInChannel(request); return; case "CCB": pa = payCache.get("CCB"); pa.payInChannel(request); return; default: throw new RuntimeException(); } }
改造之后我們先將其 AgreementPay 實例放入 map 中,然后調用時根據一個標志來選擇具體實現類。
從上面的細節我們可以看到模板方法其實設計思路與實現細節都比較簡單。看完我們的示例代碼,我們去看下 mybatis 如何使用模板方法。
mybatis 模板方法應用在看源碼之前,我們先看下我們不使用 mybatis 之前,如何查詢數據。
Class.forName("com.mysql.jdbc.Driver"); //2.獲得數據庫的連接 Connection conn = DriverManager.getConnection(URL, NAME, PASSWORD); //3.通過數據庫的連接操作數據庫,實現增刪改查 PreparedStatement pstmt = conn.prepareStatement("select user_name,age from imooc_goddess where id=?"); pstmt.setInt(1, 21); ResultSet rs = pstmt.execute(); while (rs.next()) {//如果對象中有數據,就會循環打印出來 System.out.println(rs.getString("user_name") + "," + rs.getInt("age")); }
我們可以看到直接使用 JDBC 查詢,十分麻煩,且需要我們自己將 java 類型轉換成 jdbc 數據類型。
ORM 框架重要作用在于把數據庫表與 java,ORM 框架省去我們自己將 java 類型轉化成 JDBC 類型的麻煩。JDBC 存在有那么多類型,如何做到轉換的那?其實關鍵就是應用模板設計方法。
mybatis 中存在一個接口 TypeHandler,該接口方法主要如下:
public interface TypeHandler{ void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException; T getResult(ResultSet rs, String columnName) throws SQLException; T getResult(ResultSet rs, int columnIndex) throws SQLException; T getResult(CallableStatement cs, int columnIndex) throws SQLException; }
從方法上看,這個接口主要的方法為 PreparedStatement 設置列參數,或者從 ResultSet 獲取列的值然后轉換成相應的 java 數據類型。我們看下這個接口實現的類圖。
可以看到 BaseTypeHandler 為 TypeHandler 的具體抽象類,我們具體看下 TypeHandler getResult 在抽象類中實現細節。
@Override public T getResult(ResultSet rs, String columnName) throws SQLException { T result; try { result = getNullableResult(rs, columnName); } catch (Exception e) { throw new ResultMapException("Error attempting to get column "" + columnName + "" from result set. Cause: " + e, e); } if (rs.wasNull()) { return null; } else { return result; } } public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
可以看到其最后調用抽象方法 getNullableResult。其由具體的子類的實現。我們具體找一個子類 DateTypeHandler 來查看具體實現。
public class DateTypeHandler extends BaseTypeHandler{ // 忽略其他方法 @Override public Date getNullableResult(ResultSet rs, String columnName) throws SQLException { Timestamp sqlTimestamp = rs.getTimestamp(columnName); if (sqlTimestamp != null) { return new Date(sqlTimestamp.getTime()); } return null; } }
可見其具體從 ResultSet 取出 JDBC 類型為 Timestamp,然后轉換成 java 類型的 Date。
實現具體的子類,那么在哪里使用了那?其實 mybatis 框架會把所有 TypeHandler 在 TypeHandlerRegistry 注冊。具體類方法如圖
。
其提供了相關 register 方法注冊 TypeHandler,然后又提供了相關 getTypeHandler 方法取出具體 TypeHandler 實現類。
總結使用模板方法,將公共邏輯抽取出來,將具體實現細節交給子類。
參考Mybatis源代碼分析之類型轉換
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72794.html
摘要:這些依賴對象也進一步暴露了其設計思想。關鍵功能包括在上下文內掛載在上下文外掛載在上下文外共享數據。在構造必須依賴,所以可以直接創建嵌入視圖,然后手動強制執行變更檢測。提供了兩個指令和。 @angular/material 是 Angular 官方根據 Material Design 設計語言提供的 UI 庫,開發人員在開發 UI 庫時發現很多 UI 組件有著共同的邏輯,所以他們把這些共...
摘要:行處理視圖文件名后綴。結語從源碼上看,使用了原始作為模版機制使得視圖邏輯非常簡單。無非也就是把視圖進來,用輸出緩沖把執行結果拿到即可。此文可以轉載,但轉載前需要發郵件到進行溝通,未溝通的均視作侵權。 前言 CI 的 View 沒有像 Laravel 等一些流行框架一樣設計的那么重,有自己的一套模版機制,CI 一直采用純天然的 PHP 模板形式,純天然的好處是不用再學習一套模板語言了,缺...
摘要:的組成結構部分包含了,其中部分又包含了幾個部分。代碼如下圖內容展示截圖代碼截圖快捷欄展示截圖代碼截圖頁面底部展示截圖代碼截圖上一篇之模板的學習之路源碼分析之部分下一篇之模板的學習之路源碼分析之腳本部分 body 的組成結構 body 部分包含了 HEADER、CONTAINER、FOOTER,其中 CONTAINER 部分又包含了 SIDEBAR、CONTENT、QUICK SIDEB...
摘要:當我們的視圖和數據任何一方發生變化的時候,我們希望能夠通知對方也更新,這就是所謂的數據雙向綁定。返回值返回傳入函數的對象,即第一個參數該方法重點是描述,對象里目前存在的屬性描述符有兩種主要形式數據描述符和存取描述符。 前言 談起當前前端最熱門的 js 框架,必少不了 Vue、React、Angular,對于大多數人來說,我們更多的是在使用框架,對于框架解決痛點背后使用的基本原理往往關注...
閱讀 2793·2021-09-23 11:44
閱讀 1681·2021-09-13 10:24
閱讀 2629·2021-09-08 09:36
閱讀 1238·2019-08-30 15:54
閱讀 2258·2019-08-30 13:54
閱讀 3317·2019-08-30 10:57
閱讀 1856·2019-08-29 18:43
閱讀 3622·2019-08-29 15:10