深入理解設計模式-行為型之模板(和回調區別聯系)

概述

模板設計模式(Template Design Pattern)是一種行為型設計模式,它定義了一個算法的骨架,將算法的一些步驟延遲到子類中實現。模板設計模式允許子類在不改變算法結構的情況下重新定義算法的某些步驟。

模板設計模式的核心思想是:將一個算法的主要結構定義在一個模板方法中,而將具體(某些)步驟的實現交給子類去完成。

// 模板類 抽象類:Beverage-->飲料
abstract class Beverage {// 模板方法,定義算法的骨架public final void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}// 具體步驟,由子類實現abstract void brew();abstract void addCondiments();// 公共方法void boilWater() {System.out.println("Boiling water");}void pourInCup() {System.out.println("Pouring into cup");}
}// 具體子類
class Coffee extends Beverage {void brew() {System.out.println("Dripping coffee through filter");}void addCondiments() {System.out.println("Adding sugar and milk");}
}class Tea extends Beverage {void brew() {System.out.println("Steeping the tea");}void addCondiments() {System.out.println("Adding lemon");}
}// 客戶端代碼
public class TemplateExample {public static void main(String[] args) {Beverage coffee = new Coffee();Beverage tea = new Tea();System.out.println("Making coffee:");coffee.prepareRecipe();System.out.println("\nMaking tea:");tea.prepareRecipe();}
}

在這個示例中,Beverage 是模板類,定義了模板方法 prepareRecipe(),其中包含了煮水、沖泡、倒入杯子和加調料的步驟。brew() 和 addCondiments() 是具體步驟(沖泡和加調料),由子類實現。Coffee 和 Tea 是具體子類,分別實現了不同的沖泡和調料步驟。

通過模板設計模式,模板類 Beverage 提供了一個通用的算法骨架,而具體步驟的實現交給子類。這樣可以確保算法的結構一致,同時允許不同子類根據自身特點進行實現。

使用場景、源碼應用

模板設計模式在許多場景下都可以應用,特別是在需要定義一組具有共同流程的操作時,但每個操作可能有不同的實現細節。以下是一些常見的應用場景:

  • 框架和庫:許多框架和庫使用模板設計模式來定義通用的操作流程,然后允許用戶通過子類來實現特定的操作細節。比如,數據庫操作框架可以定義一個通用的操作流程,然后用戶可以通過繼承來實現特定數據庫的連接和操作。

  • 算法實現:在某些算法中,有一些步驟是通用的,但有些步驟可能因情況而異。模板設計模式允許你將通用的步驟放在模板方法中,然后由子類來實現不同的步驟。

  • 工作流程:在工作流程管理中,可以使用模板設計模式來定義通用的工作流程,然后讓不同的流程實例來實現具體的任務。

  • 生命周期管理:在許多應用中,有一些生命周期的操作是通用的,例如初始化、清理資源等。模板設計模式可以用于定義這些通用的生命周期操作。

在源碼中,模板設計模式也有許多應用。以下是一些示例:

  • Java Servlet:在 Java Servlet 中,HttpServlet 就是一個使用模板設計模式的例子。HttpServlet 定義了 service() 方法作為模板方法,然后具體的 HTTP 請求處理由不同的子類來實現。

  • JUnit 測試框架:在 JUnit 中,測試用例的執行過程也是一個典型的模板設計模式。JUnit 提供了測試用例的生命周期方法,例如 setUp() 和 tearDown(),然后用戶可以在子類中實現這些方法來執行測試。

  • Spring Framework:在 Spring 中,JdbcTemplate 類用于執行數據庫操作,它將數據庫操作的通用流程定義在模板方法中,而具體的 SQL 執行由用戶提供的回調函數實現。

這些只是一些示例,模板設計模式在許多框架和庫中都有廣泛的應用,它提供了一種結構化的方式來定義通用的操作流程,并允許具體實現在子類中進行定制。

Java Servlet

對于 Java Web 項目開發來說,常用的開發框架是 SpringMVC。利用它,我們只需要關注業務代碼的編寫,底層的原理幾乎不會涉及。但是,如果我們拋開這些高級框架來開發 Web 項目,必然會用到 Servlet。實際上,使用比較底層的 Servlet 來開發 Web 項目也不難。我們只需要定義一個繼承 HttpServlet 的類,并且重寫其中的 doGet() 或 doPost() 方法,來分別處理 get 和 post 請求。具體的代碼示例如下所示:

public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("Hello World.");}
}

除此之外,我們還需要在配置文件 web.xml 中做如下配置。Tomcat、Jetty 等 Servlet 容器在啟動的時候,會自動加載這個配置文件中的 URL 和 Servlet 之間的映射關系。

<servlet><servlet-name>HelloServlet</servlet-name><servlet-class>com.xzg.cd.HelloServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HelloServlet</servlet-name><url-pattern>/hello</url-pattern>
</servlet-mapping>

當我們在瀏覽器中輸入網址(比如,http://127.0.0.1:8080/hello )的時候,Servlet 容器會接收到相應的請求,并且根據 URL 和 Servlet 之間的映射關系,找到相應的 Servlet(HelloServlet),然后執行它的 service() 方法service() 方法定義在父類 HttpServlet 中,它會調用 doGet() 或 doPost() 方法,然后輸出數據(“Hello world”)到網頁。我們現在來看,HttpServlet 的 service() 函數長什么樣子。

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException{HttpServletRequest  request;HttpServletResponse response;if (!(req instanceof HttpServletRequest &&res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");}request = (HttpServletRequest) req;response = (HttpServletResponse) res;service(request, response);
}protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);if (ifModifiedSince < lastModified) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);// 子類實現的擴展點doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {// 子類實現的擴展點doPost(req, resp);} else if (method.equals(METHOD_PUT)) {// 子類實現的擴展點doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {// 子類實現的擴展點doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {// 子類實現的擴展點doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {// 子類實現的擴展點doTrace(req,resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}

從上面的代碼中我們可以看出,HttpServlet 的 service() 方法就是一個模板方法,它實現了整個 HTTP 請求的執行流程,**doGet()、doPost() 是模板中可以由子類來定制的部分。**實際上,這就相當于 Servlet 框架提供了一個擴展點(doGet()、doPost() 方法),讓框架用戶在不用修改 Servlet 框架源碼的情況下,將業務代碼通過擴展點鑲嵌到框架中執行。

模板模式與Callback回調函數有何區別和聯系?

聯系:

  • 共同點:兩者都涉及將一些邏輯從調用代碼中抽離出來,使代碼更具模塊化和可維護性。
  • 抽象步驟:在模板模式中,一個通用的算法框架定義了一系列的抽象步驟,子類可以通過實現這些步驟來完成特定的行為。在回調函數中,一個函數可以接受一個回調函數作為參數,使調用者能夠在適當的時候執行這個回調函數,完成特定的操作。

區別:

  • 角色和目的:

    • 模板模式:主要目的是在超類中定義算法的骨架,而將一些具體步驟的實現推遲到子類中。它更關注整個流程的結構和控制。
    • 回調函數:主要目的是允許調用者在某個代碼塊執行時插入自己的代碼邏輯。它更關注于將執行權交給外部代碼,以便根據需要執行回調邏輯。
  • 控制權:

    • 模板模式:控制權由超類控制,子類只實現具體的步驟,流程由模板方法決定。
    • 回調函數:控制權在調用者手中,調用者通過提供回調函數來決定在何時執行回調邏輯。
  • 調用關系:

    • 模板模式:子類通過繼承超類來實現抽象步驟,超類負責調用子類的方法。
    • 回調函數:調用者將回調函數作為參數傳遞給被調用者,被調用者在適當的時候調用回調函數。

舉例:

一個具體的區別和聯系示例可以是在GUI編程中,比如在按鈕被點擊時要執行的操作。使用模板模式,你可以定義一個通用的按鈕點擊流程,包括按鈕的渲染、點擊事件的處理等。使用回調函數,你可以將點擊事件處理的邏輯作為一個回調函數傳遞給按鈕組件,以便在按鈕被點擊時執行。
Java中的 java.util.concurrent 包中的一些類使用了回調來實現多線程編程。例如,Executor 接口中的 execute(Runnable command) 方法就接受一個 Runnable 對象作為回調,用于在線程池中執行任務。

總之,模板模式和回調函數在不同的場景下有不同的應用,但都關注于提高代碼的模塊化和可重用性,同時也都涉及到將一些代碼邏輯從調用者中分離出來。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/41110.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/41110.shtml
英文地址,請注明出處:http://en.pswp.cn/news/41110.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡通信原理應用層(第五十一課)

1)DNS:域名解析系統,端口號TCP或UDP的53 2)域名注冊網站 -新網 www.xinnet.com -萬網-阿里云 www.net.cn -中國互聯 hulian.top 配置通過域名訪問網站(NETBASE第七課)_IHOPEDREAM的博客-CSDN博客 2、FTP 1)FTP概述 -文件傳輸協議 -控制連接:TCP 21 <

對redis、redisson、springcache總結

<一> redis-緩存中間件 什么是redis redis是c語言開發的&#xff0c;一個高性能key-value鍵值對內存數據庫&#xff0c;可以用來做數據庫、緩存、消息中間件的一種非關系型數據庫。 redis數據存儲在哪里 內存和磁盤中&#xff0c;但是redis的讀寫都在內存中&#xff0c;…

leetcode-413. 等差數列劃分(java)

等差數列劃分 leetcode-413. 等差數列劃分題目描述雙指針 上期經典算法 leetcode-413. 等差數列劃分 難度 - 中等 原題鏈接 - 等差數列劃分 題目描述 如果一個數列 至少有三個元素 &#xff0c;并且任意兩個相鄰元素之差相同&#xff0c;則稱該數列為等差數列。 例如&#xff0…

16 腦洞大開:GUI測試還能這么玩

頁面對象自動生成技術 頁面對象自動生成技術&#xff0c;屬于典型的“自動化你的自動化”的應用場景。它的基本思路是&#xff0c;你不用再手工維護 Page Class 了&#xff0c;只需要提供 Web 的 URL&#xff0c;它就會自動幫你生成這個頁面上所有控件的定位信息&#xff0c;并…

JMeter接口測試數據分離驅動應用

步驟&#xff1a; 創建csv文件&#xff0c;編寫接口測試用例 新建線程組——創建循環控制器&#xff08;循環次數填用例總數&#xff09; 創建CSV數據文件設置&#xff0c;設置參數。&#xff08;注意&#xff1a;是否允許帶引號&#xff1f;&#xff1a;一定要設置為true&a…

深度學習實戰48-【未來的專家團隊】基于AutoCompany模型的自動化企業概念設計與設想

大家好,我是微學AI,今天給大家介紹一下深度學習實戰48-【未來的專家團隊】基于AutoCompany模型的自動化企業概念設計與設想,文本將介紹AutoCompany模型的概念設計,涵蓋了AI智能公司的各個角色,并結合了GPT-4接口來實現各個角色的功能,設置中央控制器,公司運作過程會生成…

【MFC常用問題記錄】

MFC 記錄 MFC的edit control控件顯示1.控件添加變量M_edit后&#xff1a;2.控件ID為IDC_EDIT1: 線程函數使用 MFC的edit control控件顯示 1.控件添加變量M_edit后&#xff1a; CString str; int x 10; str.Format(_T("%d"),x); M_edit.SetWindowText(str)2.控件ID…

JMM內存模型之happens-before闡述

文章目錄 一、happens-before的定義二、happens-before的規則1. 程序順序規則&#xff1a;2. 監視器鎖規則&#xff1a;3. volatile變量規則&#xff1a;4. 傳遞性&#xff1a;5. start()規則&#xff1a;6. join()規則&#xff1a; 一、happens-before的定義 如果一個操作hap…

【編程二三事】ES究竟是個啥?

在最近的項目中&#xff0c;總是或多或少接觸到了搜索的能力。而在這些項目之中&#xff0c;或多或少都離不開一個中間件 - ElasticSearch。 今天忙里偷閑&#xff0c;就來好好了解下這個中間件是用來干什么的。 ES是什么? ? ES全稱ElasticSearch&#xff0c;是個基于Lucen…

性能優化的重要性

性能優化的重要性 性能優化的重要性摘要引言注意事項代碼示例及注釋性能優化的重要性 性能優化的重要性在 Java 中的體現響應速度資源利用效率擴展性與可維護性并發性能合理的鎖策略線程安全的數據結構并發工具類的應用避免競態條件和死鎖 總結代碼示例 博主 默語帶您 Go to Ne…

一張圖看懂 USDT三種類型地址 Omni、ERC20、TRC20的區別

USDT是當前實用最廣泛&#xff0c;市值最高的穩定幣&#xff0c;它是中心化的公司Tether發行的。在今年的4月17日之前&#xff0c;市場上存在著2種不同類型的USDT。4月17日又多了一種波場TRC20協議發行的USDT&#xff0c;它們各自有什么區別呢?哪個轉賬最快到賬&#xff1f;哪…

谷歌推出首款量子彈性 FIDO2 安全密鑰

谷歌在本周二宣布推出首個量子彈性 FIDO2 安全密鑰&#xff0c;作為其 OpenSK 安全密鑰計劃的一部分。 Elie Bursztein和Fabian Kaczmarczyck表示&#xff1a;這一開源硬件優化的實現采用了一種新穎的ECC/Dilithium混合簽名模式&#xff0c;它結合了ECC抵御標準攻擊的安全性和…

[LeetCode]矩陣對角線元素的和

解題 思路 1: 循環,找到主對角線的下標和副對角線的下標,如果矩陣長或寬為奇數的時候,需要減去中間公共的那一個值,中間公共的那個數的下標為mat[mat.size()/2][mat.size()/2]副對角線的下標為 mat [i][mat.size()-i-1] class Solution { public:int diagonalSum(vector<ve…

JVM中判定對象是否回收的的方法

引用計數法 引用計數法是一種垃圾回收&#xff08;Garbage Collection&#xff09;算法&#xff0c;用于自動管理內存中的對象。在引用計數法中&#xff0c;每個對象都有一個關聯的引用計數器&#xff0c;用于記錄對該對象的引用數量。 當一個新的引用指向對象時&#xff0c;…

Hive底層數據存儲格式

前言 在大數據領域,Hive是一種常用的數據倉庫工具,用于管理和處理大規模數據集。Hive底層支持多種數據存儲格式,這些格式對于數據存儲、查詢性能和壓縮效率等方面有不同的優缺點。本文將介紹Hive底層的三種主要數據存儲格式:文本文件格式、Parquet格式和ORC格式。 一、三…

SpringBoot復習:(42)WebServerCustomizer的customize方法是在哪里被調用的?

ServletWebServletAutoConfiguration類定義如下&#xff1a; 可以看到其中通過Import注解導入了其內部類BeanPostProcessorRegister。 BeanPostProcessor中定義的registerBeanDefinition方法會被Spring容器調用。 registerBeanDefinitions方法調用了RegistrySyntheticBeanIf…

解決vue3前端獲取文件的絕對路徑問題

解決vue3前端獲取文件的絕對路徑問題 公司的項目是基于vue3的&#xff0c;由于需求需要前端獲取用戶選的文件的絕對路徑。但是瀏覽器處于安全策略無法獲取真實的文件路徑&#xff0c;只能拿到相對路徑或者是D:\fakepath\xxxx. 看了網上很多方法都很坑&#xff0c;明明沒拿到路…

vue基礎-vue監聽當前屏幕大小做不同的操作

文章目錄 前言一、代碼如下&#xff1a;總結 前言 在vue項目開發過程中&#xff0c;有個需求&#xff0c;就是當屏幕大于1024時&#xff0c;我們默認為PC模式。小于1024時&#xff0c;我們默認為H5模式。但是有的界面我們想在PC和H5上面展示不同的數據&#xff0c;請求不同的接…

Intellij IDEA SBT依賴分析插件

可分析模塊和傳遞依賴 安裝完插件后&#xff0c;由于IDEA BUG&#xff0c;會出現兩個分析按鈕&#xff0c;一個是gradle的&#xff0c;一般是后者是新安裝的sbt。 選擇需要分析的模塊 只需要在project/plugins.sbt中添加代碼&#xff0c;啟動官方分析插件addDependencyTreeP…

1281. 整數的各位積和之差

諸神緘默不語-個人CSDN博文目錄 力扣刷題筆記 文章目錄 1. 簡單粗暴的遍歷2. 其實也是遍歷&#xff0c;但是用Python內置函數只用寫一行 1. 簡單粗暴的遍歷 Python版&#xff1a; class Solution:def subtractProductAndSum(self, n: int) -> int:he0ji1while n>1:last…