Java設計模式-模板方法模式

Java設計模式-模板方法模式

模式概述

模板方法模式簡介

核心思想:定義一個操作中的算法骨架(模板方法),將算法中某些步驟的具體實現延遲到子類中完成。子類可以在不改變算法整體結構的前提下,重定義這些步驟的行為,從而實現代碼復用與擴展的平衡。

模式類型:行為型設計模式(關注對象間的交互與職責分配)。

作用

  • 復用公共邏輯:將多個子類共有的算法步驟提取到父類,避免代碼重復。
  • 提高擴展性:子類僅需實現差異化的步驟,符合“開閉原則”(對擴展開放,對修改關閉)。
  • 規范流程:父類通過模板方法固定算法的整體結構,確保子類行為的一致性。

典型應用場景

  • 多個子類有公共的行為邏輯,但部分步驟實現不同(如數據庫訪問:連接、執行SQL、關閉連接的流程固定,但不同數據庫的驅動實現不同)。
  • 框架中需要控制子類的執行流程(如Spring的JdbcTemplate封裝了JDBC操作的通用流程,具體SQL執行由子類或回調實現)。
  • 需要約束子類的行為,確保關鍵步驟不被遺漏(如訂單處理流程:下單→支付→發貨→通知,其中支付方式可自定義)。

我認為:模板方法模式是“流程標準化”與“步驟定制化”的完美結合,父類搭骨架,子類填細節。

課程目標

  • 理解模板方法模式的核心思想和經典應用場景
  • 識別應用場景,使用模板方法模式解決功能要求
  • 了解模板方法模式的優缺點

核心組件

角色-職責表

角色職責示例類名
抽象模板角色定義模板方法(算法骨架)和基本方法(具體方法、抽象方法、鉤子方法)AbstractBeverageMaker
具體模板角色繼承抽象模板角色,實現所有抽象方法,并可選重寫鉤子方法CoffeeMakerTeaMaker

類圖

下面是一個簡化的類圖表示,展示了模板方法模式中的主要角色及其交互方式:

繼承
繼承
?abstract?
AbstractBeverageMaker
+final void makeBeverage()
-void boilWater()
-abstract void brew()
-void pourInCup()
-boolean needAddCondiments()
-void addCondiments()
CoffeeMaker
+void brew()
+boolean needAddCondiments()
+void addCondiments()
TeaMaker
+void brew()

傳統實現 VS 模板方法模式

案例需求

案例背景:實現飲料制作功能(如咖啡、茶),通用流程為:燒水→沖泡→倒入杯子→添加調料(可選)。不同飲料的沖泡方式(如咖啡粉 vs 茶葉)和調料添加(如加糖 vs 不加)不同。

傳統實現(痛點版)

代碼實現

// 傳統實現:每個飲料獨立編寫完整流程
class CoffeeMaker {public void makeCoffee() {boilWater();  // 重復代碼brewCoffee(); // 咖啡特有邏輯pourInCup();  // 重復代碼addSugar();   // 咖啡特有邏輯}private void boilWater() {System.out.println("燒水:煮沸100℃");}private void brewCoffee() {System.out.println("沖泡:用熱水沖咖啡粉");}private void pourInCup() {System.out.println("倒入杯子");}private void addSugar() {System.out.println("添加:糖和牛奶");}
}class TeaMaker {public void makeTea() {boilWater();  // 重復代碼brewTea();    // 茶葉特有邏輯pourInCup();  // 重復代碼// 茶不需要調料,無需添加}private void boilWater() {System.out.println("燒水:煮沸100℃"); // 重復代碼}private void brewTea() {System.out.println("沖泡:用熱水泡茶葉");}private void pourInCup() {System.out.println("倒入杯子"); // 重復代碼}
}

痛點總結

  • 代碼冗余boilWater()pourInCup()等方法在每個子類中重復實現。
  • 擴展性差:新增飲料(如果汁)需復制大量重復代碼,違反開閉原則。
  • 流程不可控:無法保證所有飲料遵循相同的基礎流程(如漏掉“倒入杯子”步驟)。

模板方法模式 實現(優雅版)

代碼實現

// 抽象模板角色:定義流程骨架
abstract class AbstractBeverageMaker {// 模板方法(final修飾,防止子類修改流程)public final void makeBeverage() {boilWater();brew();       // 調用抽象方法(子類實現)pourInCup();if (needAddCondiments()) {  // 調用鉤子方法(控制是否添加調料)addCondiments();}}// 具體方法(通用邏輯)private void boilWater() {System.out.println("燒水:煮沸100℃");}// 抽象方法(子類必須實現)protected abstract void brew();// 具體方法(通用邏輯)private void pourInCup() {System.out.println("倒入杯子");}// 鉤子方法(默認不添加調料,子類可選重寫)protected boolean needAddCondiments() {return false;}// 鉤子方法關聯的具體操作(子類可選重寫)protected void addCondiments() {// 默認空實現}
}// 具體模板角色:咖啡制作
class CoffeeMaker extends AbstractBeverageMaker {@Overrideprotected void brew() {System.out.println("沖泡:用熱水沖咖啡粉");}@Overrideprotected boolean needAddCondiments() {return true;  // 咖啡需要添加調料}@Overrideprotected void addCondiments() {System.out.println("添加:糖和牛奶");}
}// 具體模板角色:茶葉制作
class TeaMaker extends AbstractBeverageMaker {@Overrideprotected void brew() {System.out.println("沖泡:用熱水泡茶葉");}// 不重寫needAddCondiments(),默認不添加調料
}

優勢

  • 消除冗余:公共方法(如boilWater())在抽象類中實現,子類無需重復。
  • 流程可控:模板方法通過final修飾,確保子類無法修改基礎流程。
  • 靈活擴展:子類僅需實現抽象方法(如brew()),并通過鉤子方法(needAddCondiments())控制可選邏輯。

局限

  • 類數量增加:每個差異化的子類需單獨定義,可能增加系統復雜度。
  • 抽象類設計成本:需合理規劃抽象方法與鉤子方法,過度設計可能導致冗余。

模式變體

  • 具體模板方法:將模板方法聲明為final,禁止子類修改算法骨架(強制遵循固定流程)。
  • 鉤子方法(Hook Method):提供默認實現的方法(通常返回布爾值或空操作),子類可選擇是否重寫以影響模板方法的行為(如上述案例中的needAddCondiments())。
  • 參數化模板:在模板方法中添加參數,允許子類通過參數調整行為(如數據庫操作模板支持傳入事務隔離級別)。

最佳實踐

建議理由
抽象類保持穩定模板方法模式的核心是流程固定,頻繁修改抽象類會導致所有子類連鎖改動。
鉤子方法提供默認實現減少子類必須重寫的負擔,僅當需要差異化時才覆蓋。
避免過度抽象若子類間差異極小(如僅有1個步驟不同),可能更適合直接繼承而非模板模式。
模板方法用final修飾防止子類意外修改算法骨架,確保流程一致性。

一句話總結

模板方法模式通過“父類定義流程骨架,子類實現差異化步驟”,在保證流程規范的同時,實現了代碼復用與靈活擴展。

如果關注Java設計模式內容,可以查閱作者的其他Java設計模式系列文章。😊

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

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

相關文章

Centos7物理安裝 Redis8.2.0

Centos7物理安裝 Redis8.2.0一、準備依賴環境首先安裝編譯 Redis 所需的依賴:# CentOS/RHEL系統 yum install -y gcc gcc-c make wget 二、下載并編譯 Redis 8.2.0# 1. 下載Redis 8.2.0源碼包 wget https://download.redis.io/releases/redis-8.2.0.tar.gz# 2. 解壓…

牛津大學xDeepMind 自然語言處理(3)

條件語言模型無條件語言模型 概率計算:通過鏈式法則分解為預測下一詞概率(將語言建模問題簡化為建模給定前面詞語歷史的下一個詞的概率)基于循環神經網絡的無條件語言模型:根據歷史詞語預測下一個詞的概率條件語言模型 定義&#…

Vue2.x核心技術與實戰(一)

目錄 一、Vue2.x:快速上手+插值表達式+指令上 1.1 Vue快速上手 1.1.1 Vue概念 1.1.2 創建實例 1.1.3 插值表達式 { { }} 1.1.4 響應式特性 1.1.5 開發者工具 1.2 Vue指令 1.2.1 v-html 1.2.3 v-show / v-if v-show v-if 1.2.4 v-else / v-else-if 1.2.5 v-on v…

SCAU學習筆記 - 自科三面前端方向實戰演示

本來是準備寫完二面直接開始寫算法三面的,maimai那個封面圖我都做好了。但是可惡的出題人說要等我出完解析再針對性避開出題,所以swan決定把那個先擱置,本文我們先以2023年的自科三面前端方向題為例帶各位快速入門前端三件套(因為…

前后端聯合實現文件上傳,實現 SQL Server image 類型文件上傳

1、前端 Vue3QualityFileInfoDialog.vue<script setup lang"ts" name"QualityFile"> ...... // 上傳&#xff0c;防抖 const onUploadClick debounce(() > {// 模擬點擊元素if (fileInputRef.value) {// 重置以允許重復選擇相同文件fileInputRef…

使用安卓平板,通過USB數據線(而不是Wi-Fi)來控制電腦(版本1)

這是一個對延遲和穩定性要求很高的場景。 核心原理是&#xff1a;利用USB數據線&#xff0c;在手機和電腦之間創建一個高速的“虛擬網絡連接”&#xff0c;然后在這個穩定的網絡通道上運行遠程控制軟件。 方案1&#xff1a; 在完全沒有無線網絡&#xff08;Wi-Fi&#xff09;和…

linux報permission denied問題

linux報permission denied問題 一般是沒有可執行權限&#xff0c;需要先添加執行權限 1. 確認文件權限 在你的項目目錄下執行&#xff1a; ls -l ./folder你可能會看到類似&#xff1a; -rw-r--r-- 1 user user 1234 Aug 18 12:00 script.sh注意&#xff1a;這里缺少 x&#xf…

Vue深入組件:組件事件詳解2

聲明觸發的事件 為了讓組件的用法更清晰(作為文檔),同時讓 Vue 能區分事件與透傳 attribute,推薦顯式聲明組件要觸發的事件。根據組件是否使用 <script setup>,聲明方式有所不同。 使用 <script setup> 時:defineEmits() 宏 在 <script setup> 中,…

FLASK項目快速構建

Flask 項目構建 exts.py # flask_sqlalchemy from flask_sqlalchemy import SQLAlchemy from flask_mail import Mail from flask_caching import Cache from flask_wtf import CSRFProtect from flask_avatars import Avatars from flask_jwt_extended import JWTManager from…

數據結構--2:ArrayList與順序表

1.順序表的創建 2.常見操作 3.遍歷 4.擴容機制 5.例子1.順序表的創建在集合框架中&#xff0c;ArrayList是?個普通的類&#xff0c;實現了List接口&#xff0c;具體框架圖如下&#xff1a;2.常見操作代碼…

【Kubesphere】K8s容器無法訪問內網xx網絡問題

問題遇到的現象和發生背景 Kubesphere中運行的一個容器&#xff0c;可以ping通我們公司內網網段172.16.XX.XX&#xff0c;但是在容器內無法ping通192.168.5.XX&#xff0c;但是我在宿主機是可以ping通192.168.5.XX&#xff0c;這個192.168.5.XX是通過xx設備接進來的&#xff0c…

【開發語言】Groovy語言:Java生態中的動態力量

博客目錄一、Groovy 的誕生與發展二、核心特性深度解析1. 與 Java 的無縫集成2. 動態類型與可選靜態類型3. 強大的集合操作三、Groovy 在實際開發中的應用場景1. 構建自動化&#xff08;Gradle&#xff09;2. 測試開發&#xff08;Spock 框架&#xff09;3. 腳本任務自動化四、…

Obsidian 1.9.10升級

概述 Obsidian發布了更新版本1.9.10&#xff0c;是一次比較大的升級&#xff0c;尤其是增加了一些以前沒有的核心插件&#xff0c;尤其是重磅的數據庫功能。雖然可能還是比較初期&#xff0c;但是這意味著OB還是往更好的方向進化了。 本文以一些目前的視頻教程加自己的實際上手…

內容審計技術

一、 內容審計需求背景1.網絡安全法要求明確責任人&#xff1a;制定內部安全管理制度和操作規程&#xff0c;落實安全保護責任。監測、記錄并保留日志&#xff1a;采取監測、記錄網絡運行狀態、網絡安全事件的技術措施&#xff0c;并按照規定留存相關網絡日志不少于六個月。采取…

反序列化漏洞

php反序列化 1.什么是序列化和反序列化 office word是程序 doc/docx是數據 保存word文件&#xff1a;程序--保存(序列化)-->數據文件 打開word文件&#xff1a;程序--加載數據文件-->還原(反序列化) 游戲存檔&#xff1a;角色等級&#xff0c;任務&#xff0c;人物坐…

Lecture 4 Mixture of experts課程筆記

什么是MoE?用&#xff08;多個&#xff09;大型前饋網絡和一個選擇器層取代大型前饋網絡。你可以在不影響浮點運算次數的情況下增加專家數量。 MoE受歡迎的原因 相同的浮點運算次數&#xff0c;更多的參數表現更好訓練混合專家模型&#xff08;MoEs&#xff09;速度更快訓練混…

微服務架構的演進:從 Spring Cloud Netflix 到云原生新生態

過去十年,Spring Cloud 憑借 Netflix 全家桶(Eureka、Ribbon、Hystrix、Zuul 等)幾乎成為 Java 微服務的事實標準。但隨著這些核心組件逐步停止更新或進入維護模式,微服務架構正經歷一場深刻的演進。新的微服務架構更加注重 云原生兼容性、社區活躍度、企業級穩定性和低運維…

網絡流量分析——基礎知識

文章目錄所需技能和知識TCP/IP 堆棧和 OSI 模型基本網絡概念常用端口和協議IP 數據包和子層的概念協議傳輸封裝環境與設備常見的流量分析工具BPF 語法執行網絡流量分析NTA工作流程NTA工作流程網絡 - 第 1-4 層OSI / TCP-IP 模型尋址機制MAC地址IP 尋址IPv4IPv6IPv6 尋址類型IPv…

ansible playbook 實戰案例roles | 實現基于 IHS 的 AWStats 訪問監控系統

文章目錄一、核心功能描述二、roles內容2.1 文件結構2.2 主配置文件2.3 tasks文件內容三、files文件內容四、關鍵價值免費個人運維知識庫&#xff0c;歡迎您的訂閱&#xff1a;literator_ray.flowus.cn 一、核心功能描述 這個 Ansible Role 的核心功能是&#xff1a;?實現 ?…

DELL服務器 R系列 IPMI的配置

1、iDRAC功能默認都是關閉&#xff0c;需要在BIOS面啟用&#xff0c;首先重啟計算機&#xff0c;按F2然后進入BIOS&#xff0c;選擇iDRAC Setting進行iDRAC配置 2、重置一下idrac卡-重置才能恢復默認密碼 3、進入iDRAC Setting之后&#xff0c;選擇設置網絡Network 4、啟用iDRA…