深入理解設計模式之模板模式:優雅地定義算法骨架

在軟件開發中,我們經常會遇到這樣的情況:多個類執行相似的操作流程,但每個類在流程的某些步驟上有自己特定的實現。如果為每個類都完整地編寫整個流程,會導致大量重復代碼,且難以維護。這時候,模板模式(Template Method Pattern)就派上用場了。

一、模板模式概述

1.1 什么是模板模式

模板模式是一種行為型設計模式,它定義了一個操作中的算法骨架,而將一些步驟延遲到子類中實現。模板方法使得子類可以不改變算法結構的情況下,重新定義算法中的某些特定步驟。

簡單來說,模板模式就是把不變的流程放在父類中實現,把可能變化的具體實現延遲到子類中完成。這就像我們寫文檔時使用的模板——文檔的結構是固定的,但具體內容可以根據需要填充。

1.2 模式結構

模板模式通常包含以下幾個關鍵組成部分:

  1. 抽象類(Abstract Class)

    • 定義了一個或多個抽象操作(稱為基本操作),這些操作由子類實現

    • 實現了一個模板方法,定義算法的骨架,按順序調用基本操作

    • 可能包含一些具體方法(有默認實現)和鉤子方法

  2. 具體類(Concrete Class)

    • 實現抽象類中定義的基本操作

    • 可以選擇性地覆蓋鉤子方法

1.3 模式特點

  • 模板方法:定義算法骨架,通常聲明為final以防止子類重寫

  • 基本操作:抽象方法或具體方法,由子類實現或覆蓋

  • 鉤子方法:提供默認實現,子類可選擇是否覆蓋

二、模板模式的實現

2.1 基本實現示例

讓我們通過一個簡單的例子來理解模板模式的實現。假設我們有一個制作飲料的流程,沖泡咖啡和茶的流程相似但不完全相同。

// 抽象類 - 飲料制作
public abstract class Beverage {// 模板方法,定義制作飲料的流程(final防止子類修改流程)public final void prepareBeverage() {boilWater();brew();pourInCup();if (customerWantsCondiments()) {addCondiments();}}// 基本方法 - 煮沸水(共用方法)private void boilWater() {System.out.println("煮沸水");}// 基本方法 - 倒入杯子(共用方法)private void pourInCup() {System.out.println("倒入杯子");}// 抽象方法 - 沖泡(由子類實現)protected abstract void brew();// 抽象方法 - 添加調料(由子類實現)protected abstract void addCondiments();// 鉤子方法 - 客戶是否需要調料(默認需要)protected boolean customerWantsCondiments() {return true;}
}// 具體類 - 咖啡
public class Coffee extends Beverage {@Overrideprotected void brew() {System.out.println("沖泡咖啡粉");}@Overrideprotected void addCondiments() {System.out.println("加入糖和牛奶");}@Overrideprotected boolean customerWantsCondiments() {// 可以通過用戶輸入決定是否添加調料String answer = getUserInput();return answer.toLowerCase().startsWith("y");}private String getUserInput() {// 實際項目中可能是從界面獲取用戶輸入return "yes";}
}// 具體類 - 茶
public class Tea extends Beverage {@Overrideprotected void brew() {System.out.println("浸泡茶葉");}@Overrideprotected void addCondiments() {System.out.println("加入檸檬");}// 不覆蓋customerWantsCondiments(),使用默認實現
}

2.2 代碼解析

在這個例子中:

  1. Beverage?是抽象類,定義了制作飲料的模板方法?prepareBeverage()

  2. boilWater()?和?pourInCup()?是具體方法,所有飲料共用

  3. brew()?和?addCondiments()?是抽象方法,由子類實現

  4. customerWantsCondiments()?是鉤子方法,子類可以選擇覆蓋

  5. Coffee?和?Tea?是具體類,實現了特定的沖泡和添加調料方法

2.3 模板方法中的鉤子方法

鉤子方法(Hook Method)是模板模式中的一個重要概念。它是一個在抽象類中聲明并提供默認實現的方法,子類可以選擇性地覆蓋它。鉤子方法通常用于:

  1. 對模板方法的流程進行微調

  2. 為子類提供額外的擴展點

  3. 控制某些可選步驟是否執行

在上面的例子中,customerWantsCondiments()?就是一個鉤子方法,它控制是否執行?addCondiments()?步驟。

三、模板模式的深入分析

3.1 優點

  1. 提高代碼復用性:將公共代碼移到父類中,避免了代碼重復

  2. 實現反向控制:通過父類調用子類的操作,符合"好萊塢原則"("不要調用我們,我們會調用你")

  3. 便于擴展:符合開閉原則,增加新的具體類很容易

  4. 提高可維護性:算法結構集中在一個地方,修改方便

  5. 靈活性:通過鉤子方法提供額外的控制點

3.2 缺點

  1. 類數量增加:每個不同的實現都需要一個子類

  2. 設計復雜度增加:需要仔細設計抽象類和具體類的關系

  3. 繼承的局限性:Java等語言只支持單繼承,限制了靈活性

  4. 可能導致方法泛濫:如果基本操作過多,會導致類變得復雜

3.3 適用場景

模板模式適用于以下情況:

  1. 一次性實現算法的不變部分,將可變部分留給子類實現

  2. 各子類中公共的行為應被提取出來集中到一個公共父類中

  3. 需要控制子類擴展,只允許在特定點進行擴展

  4. 多個類有相似的行為,但某些步驟的實現不同

四、模板模式在實際中的應用

4.1 Java集合框架中的模板模式

Java的AbstractListAbstractSetAbstractMap等類都使用了模板模式。它們提供了集合操作的骨架實現,具體的集合類只需要實現少量必要的方法。

例如,AbstractList提供了iterator()contains()等方法的默認實現,這些方法依賴于get(int)size()等抽象方法,由具體子類實現。

4.2 Servlet中的模板模式

在Java Web開發中,HttpServlet類使用了模板模式。它提供了service()方法作為模板方法,根據HTTP請求類型調用相應的doGet()doPost()等方法。開發者只需要覆蓋需要處理的HTTP方法即可。

4.3 Spring框架中的模板模式

Spring框架大量使用了模板模式,最典型的是JdbcTemplate。它封裝了JDBC操作的固定流程(獲取連接、創建語句、執行SQL、處理結果、釋放資源),而開發者只需要提供SQL和結果處理邏輯。

jdbcTemplate.query("SELECT * FROM users WHERE age > ?", new Object[]{18},(rs, rowNum) -> new User(rs.getInt("id"),rs.getString("name"),rs.getInt("age"))
);

在這個例子中,Spring處理了所有樣板代碼(異常處理、資源清理等),開發者只需要關注SQL和結果映射。

五、模板模式的最佳實踐

5.1 設計原則

  1. 好萊塢原則:"不要調用我們,我們會調用你"——父類控制流程,子類提供具體實現

  2. 開閉原則:對擴展開放(通過子類實現新行為),對修改關閉(不修改模板方法)

  3. 單一職責原則:每個類只關注自己的特定實現

5.2 實現建議

  1. 將模板方法聲明為final:防止子類改變算法結構

  2. 盡量減少基本操作的數量:太多抽象方法會使子類實現變得復雜

  3. 合理使用鉤子方法:提供靈活性的同時不要過度使用

  4. 命名約定:可以給模板方法添加"Template"后綴以提高可讀性

  5. 考慮使用組合代替繼承:在某些情況下,策略模式可能是更好的選擇

5.3 與其他模式的關系

  1. 與工廠方法模式:工廠方法模式常被模板方法調用

  2. 與策略模式:都是封裝算法,但策略模式使用組合,模板模式使用繼承

  3. 與裝飾器模式:裝飾器模式動態添加行為,模板模式靜態定義算法骨架

六、總結

模板模式是一種強大而靈活的設計模式,它通過定義算法的骨架并將具體步驟延遲到子類中實現,達到了代碼復用和擴展性的平衡。在實際開發中,當遇到多個類有相似流程但某些步驟實現不同的情況時,考慮使用模板模式可以顯著提高代碼質量和可維護性。

然而,模板模式也有其局限性,特別是它對繼承的依賴。在現代軟件開發中,組合優于繼承的原則越來越被重視,因此在某些場景下,可以考慮使用策略模式等替代方案。

理解并合理運用模板模式,可以幫助我們設計出更加清晰、靈活和可維護的代碼結構,是每個軟件工程師工具箱中的重要工具之一。

?

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

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

相關文章

基于單片機寵物喂食器/智能寵物窩/智能飼養

傳送門 👉👉👉👉其他作品題目速選一覽表 👉👉👉👉其他作品題目功能速覽 概述 深夜加班時,你是否擔心家中寵物餓肚子?出差旅途中,是否焦慮寵…

靜態補丁腳本 - 修改 libtolua.so

直接改arm64的so, 使用python腳本。#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 靜態補丁腳本 - 修改 libtolua.so 主要功能: 1. 修改 luaL_loadbuffer 函數,將跳轉目標從 luaL_loadbufferx 改為 luaL_loadfilex 2. …

2-大語言模型—理論基礎:詳解Transformer架構的實現(2)

目錄 1-大語言模型—理論基礎:詳解Transformer架構的實現(1)-CSDN博客https://blog.csdn.net/wh1236666/article/details/149443139?spm1001.2014.3001.5502 2.3、殘差連接和層歸一化 2.3.1、什么是層歸一化? 2.3.2、層歸一化的核心特點&#xff08…

SmartX 用戶建云實踐|富士康:基于榫卯企業云平臺構建分布式云,支撐全球多地工廠重要產線

作為全球最大的電子科技智造服務商,富士康集團在全球范圍內構建生產制造網絡。為實現多廠區統一管理與降本增效,在逐步替代 VMware 虛擬化架構的過程中,富士康對比了自研 OpenStack Ceph 平臺和 SmartX 超融合方案,最終選擇基于 …

ADC選型設計

1、最大擺伏FSR: 0 ~ 4.096V,一般Vref要等于FSR 2、最大頻率:根據奈奎斯特采樣定理大于2倍的信號頻率才夠還原信號,所以選擇20/50倍更好, 3、最小精度,對于一給定模擬輸入,實際數字輸出與理論預…

基于深度學習的火災智能檢測系統設計與實現

在各類安全事故中,火災因其突發性強、破壞力大,一直是威脅人們生命財產安全的重大隱患。傳統的火災檢測方式多依賴煙霧傳感器、溫度傳感器等,存在響應滯后、易受環境干擾等問題。隨著深度學習技術的飛速發展,基于計算機視覺的火災…

HIVE實戰處理(二十四)留存用戶數

留存概念: 次X日活躍留存,次X日新增留存,也就是看今天的新增或活躍用戶在后續幾天的留存情況一、留存表的生成邏輯 因為用戶活躍日期和留存的日期無法對齊所以搞了2級分區(dt,static_day) 1)首先獲得計算日D、根據要出…

W3C XHTML 活動:標準化的未來與交互式體驗

W3C XHTML 活動:標準化的未來與交互式體驗 概述 W3C(World Wide Web Consortium)是全球領先的互聯網技術標準制定組織。XHTML,作為W3C推薦的標準之一,是一種基于XML的標記語言,旨在提供一個更加結構化、兼容性和可擴展性更高的網頁內容表示方式。本文將圍繞W3C的XHTML活…

Java-數構鏈表

1.鏈表 1.1鏈表的概念和結構 鏈表是一種物理存儲結構上非連續存儲結構,數據元素的邏輯順序是通過鏈表中引用鏈接次序實現的。 這里大多討論無頭單向非循環鏈表。這種結構,結構簡單,一般與其他數據結構結合,作為其他數據結構的子…

Windows系統軟件游戲丟失找不到mgmtapi.dll修復解決方法

在使用電腦系統時經常會出現丟失找不到某些文件的情況,由于很多常用軟件都是采用 Microsoft Visual Studio 編寫的,所以這類軟件的運行需要依賴微軟Visual C運行庫,比如像 QQ、迅雷、Adobe 軟件等等,如果沒有安裝VC運行庫或者安裝…

初識C++——開啟新旅途

從今天開始主包也是掉入C這個深坑,上完課也是跟沒上一樣,所以寫好博客復習還是很重要的,話不多說,進入正題~~1、命名空間(1)namespace的價值與作用在C/C中,變量、函數和后面要學到的類都是大量存在的,這些變…

vue2 面試題及詳細答案150道(141 - 150)

《前后端面試題》專欄集合了前后端各個知識模塊的面試題,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs&…

第十三章 Go包管理

文章目錄使用logurs處理程序日志logrus 常用配置使用viper處理程序配置使用logurs處理程序日志 下載包,在終端執行命令 go get github.com/sirupsen/logrus官方示例 package mainimport (log "github.com/sirupsen/logrus" )func main() {log.WithFiel…

EP01:【Python 第一彈】基礎入門知識

一、基礎入門知識 1.1 代碼規范 1.1.1 語句分隔符 ; 換行 1.1.2 格式化 對 Windows 和 Linux 操作系統,快捷鍵是Ctrl Alt L對 macOS 操作系統,快捷鍵是Cmd Option L 1.1.3 注釋 單行注釋 # 這是一行注釋多行注釋 """ 這 是 …

實用的文件和文件夾批量重命名工具

在日常工作中,文件和文件夾的命名管理常常讓人頭疼。尤其是面對大量文件時,手動重命名不僅耗時,還容易出錯。今天,我要給大家推薦一款超級實用的工具——OncePower 文件批量重命名,它不僅能批量重命名文件和文件夾&…

【Git】報錯:git config --global http.sslBackend “openssl“

問題解決 報錯:git config --global http.sslBackend “openssl”解決方法: git config --global http.sslBackend "openssl"之后再 push 即可正常提交。 🔍 原因分析 ??系統環境不支持 OpenSSL 后端?? Git 在某些平臺&#xf…

Redisson RLocalCachedMap 核心參詳解

🧑 博主簡介:CSDN博客專家,歷代文學網(PC端可以訪問:https://literature.sinhy.com/#/?__c1000,移動端可微信小程序搜索“歷代文學”)總架構師,15年工作經驗,精通Java編…

AI輔助編程時代的高效規范開發指南:工具、原則與提效策略

引言:AI輔助編程的時代背景與核心挑戰 人工智能在編程領域的應用雖可追溯至20世紀50年代,但近十年實現了革命性突破,推動其從早期的代碼補全工具演進為能理解上下文、生成完整函數乃至項目架構的智能系統。關鍵發展里程碑包括:20…

百度網盤TV版1.21.0 |支持倍速播放,大屏云看片

百度網盤TV版是專為智能電視設計的應用程序,讓用戶可以直接在大屏幕上觀看保存在云端的視頻資源。此應用提供了與手機端幾乎相同的功能,包括倍速播放功能,使得用戶可以更方便地享受高清視頻內容。無需繁瑣的操作步驟,即可實現云端…

C++控制臺貪吃蛇開發(二):讓靈蛇舞動起來!

資料合集下載鏈接: ??https://pan.quark.cn/s/472bbdfcd014? 本文將深入講解蛇移動的機制,并帶你一步步實現以下功能: 理解蛇移動的核心算法:為什么蛇的移動是“倒著”更新的? 用代碼表示方向:如何使用??dx??和??dy??變量優雅地控制方向。 編寫核心??move…