深入理解模板方法模式:框架設計的“骨架”藝術

目錄

前言

一、模板方法模式的核心思想

二、模板方法模式的結構組成

1. 抽象類(Abstract Class)

2. 具體子類(Concrete Class)

三、C++ 實現示例:咖啡與茶的制作流程

步驟 1:定義抽象類(飲料基類)

步驟 2:實現具體子類(咖啡和茶)

步驟 3:測試代碼

四、模板方法模式的優缺點(C++ 視角)

優點

缺點

五、C++ 中模板方法模式的應用場景

六、C++ 中模板方法模式的注意事項

七、總結


前言

在面向對象設計中,我們經常會遇到這樣的場景:多個類實現相似的流程,但部分步驟存在差異。如果為每個類重復編寫相同的流程代碼,不僅會導致冗余,還會增加維護成本。模板方法模式正是為解決這類問題而生的 —— 它通過定義統一的流程骨架,將可變步驟延遲到子類實現,完美平衡了流程標準化細節差異化

本文將結合 C++ 代碼,深入解析模板方法模式的原理、實現與應用。

一、模板方法模式的核心思想

模板方法模式是一種經典的行為型設計模式,其核心可以概括為:“定義算法骨架,延遲具體實現”

想象一個場景:制作咖啡和茶的流程高度相似(煮水→沖泡→裝杯→加調料),但 “沖泡” 和 “加調料” 的具體操作不同。模板方法模式會將這些相同的流程步驟抽象到父類,而將不同的步驟聲明為抽象方法,由子類(咖啡、茶)各自實現。

用 C++ 的視角來看,這種模式通過抽象類 + 繼承實現:

  • 抽象類負責定義 “流程骨架”(模板方法);
  • 子類負責實現 “可變細節”(抽象方法)。

二、模板方法模式的結構組成

模板方法模式主要包含兩個角色,在 C++ 中通常通過類層次結構實現:

1. 抽象類(Abstract Class)

  • 模板方法(Template Method):一個非虛成員函數(通常用final修飾,防止子類重寫),定義算法的步驟順序,調用其他方法(包括抽象方法和具體方法)。
  • 抽象方法(Primitive Operations):純虛函數,由子類實現的可變步驟。
  • 具體方法(Concrete Methods):普通成員函數,算法中固定不變的步驟(子類無需修改)。
  • 鉤子方法(Hook Methods):虛函數(有默認實現),子類可選擇性重寫,用于控制模板流程的分支(可選)。

2. 具體子類(Concrete Class)

  • 繼承抽象類,實現所有純虛的抽象方法;
  • 可選重寫鉤子方法,調整流程的執行邏輯。

三、C++ 實現示例:咖啡與茶的制作流程

下面我們用 C++ 實現一個經典案例:咖啡和茶的制作。兩者的流程相似,但 “沖泡” 和 “加調料” 的步驟不同,適合用模板方法模式封裝。

步驟 1:定義抽象類(飲料基類)

首先創建抽象類Beverage,它包含制作飲料的流程骨架和相關方法:

// Beverage.h
#ifndef BEVERAGE_H
#define BEVERAGE_H#include <iostream>
using namespace std;class Beverage {
public:// 模板方法:定義制作流程的骨架,用final防止子類修改(C++11及以上支持)void prepareRecipe() final {boilWater();       // 固定步驟:煮水brew();            // 抽象方法:沖泡(子類實現)pourInCup();       // 固定步驟:倒入杯子addCondiments();   // 抽象方法:加調料(子類實現)if (customerWantsCondiments()) {  // 鉤子方法:判斷是否加額外調料addExtraCondiments();         // 可選步驟:加額外調料}}protected:// 抽象方法:沖泡(純虛函數,子類必須實現)virtual void brew() = 0;// 抽象方法:加調料(純虛函數,子類必須實現)virtual void addCondiments() = 0;// 鉤子方法:默認返回true(加額外調料),子類可重寫virtual bool customerWantsCondiments() {return true;}private:// 具體方法:固定步驟——煮水(子類無需修改)void boilWater() {cout << "煮水至沸騰" << endl;}// 具體方法:固定步驟——倒入杯子(子類無需修改)void pourInCup() {cout << "倒入杯中" << endl;}// 具體方法:可選步驟——加額外調料(固定邏輯)void addExtraCondiments() {cout << "添加額外糖/奶" << endl;}
};#endif // BEVERAGE_H

代碼說明

  • prepareRecipe()是模板方法,用final確保子類無法修改流程順序;
  • brew()addCondiments()是純虛函數(抽象方法),必須由子類實現;
  • customerWantsCondiments()是鉤子方法,提供默認實現,子類可重寫以改變流程分支;
  • boilWater()pourInCup()等是具體方法,封裝固定步驟,子類無需關心。

步驟 2:實現具體子類(咖啡和茶)

接下來創建CoffeeTea類,繼承Beverage并實現抽象方法:

Coffee

// Coffee.h
#ifndef COFFEE_H
#define COFFEE_H#include "Beverage.h"class Coffee : public Beverage {
protected:// 實現抽象方法:咖啡的沖泡邏輯void brew() override {cout << "用沸水沖泡咖啡粉" << endl;}// 實現抽象方法:咖啡的加調料邏輯void addCondiments() override {cout << "加牛奶和糖" << endl;}// 重寫鉤子方法:咖啡默認不加額外調料bool customerWantsCondiments() override {return false; // 不執行addExtraCondiments()}
};#endif // COFFEE_H

Tea

// Tea.h
#ifndef TEA_H
#define TEA_H#include "Beverage.h"class Tea : public Beverage {
protected:// 實現抽象方法:茶的沖泡邏輯void brew() override {cout << "用沸水浸泡茶葉" << endl;}// 實現抽象方法:茶的加調料邏輯void addCondiments() override {cout << "加檸檬" << endl;}// 不重寫鉤子方法,使用父類默認邏輯(加額外調料)
};#endif // TEA_H

代碼說明

  • CoffeeTea分別實現了brew()addCondiments(),定義各自的差異化步驟;
  • Coffee重寫了鉤子方法customerWantsCondiments(),返回false以跳過 “加額外調料” 步驟;
  • Tea未重寫鉤子方法,使用父類默認實現(返回true),因此會執行 “加額外調料”。

步驟 3:測試代碼

最后編寫測試代碼,驗證模板方法的執行效果:

// main.cpp
#include "Coffee.h"
#include "Tea.h"int main() {// 制作咖啡Beverage* coffee = new Coffee();cout << "制作咖啡:" << endl;coffee->prepareRecipe();// 制作茶Beverage* tea = new Tea();cout << "\n制作茶:" << endl;tea->prepareRecipe();// 釋放資源delete coffee;delete tea;return 0;
}

輸出結果

制作咖啡:
煮水至沸騰
用沸水沖泡咖啡粉
倒入杯中
加牛奶和糖制作茶:
煮水至沸騰
用沸水浸泡茶葉
倒入杯中
加檸檬
添加額外糖/奶

結果分析

  • 咖啡和茶都遵循了prepareRecipe()定義的流程骨架;
  • 差異化步驟(brew()addCondiments())按子類實現執行;
  • 鉤子方法控制了流程分支:咖啡跳過 “加額外調料”,茶則執行了該步驟。

四、模板方法模式的優缺點(C++ 視角)

優點

  1. 代碼復用:將公共流程(如boilWater())抽取到抽象類,減少重復代碼;
  2. 符合開閉原則:新增飲料(如奶茶)只需繼承Beverage并實現抽象方法,無需修改父類;
  3. 流程可控:父類通過final修飾模板方法,避免子類破壞流程邏輯;
  4. 邏輯清晰:算法的核心步驟在父類中集中體現,便于維護。

缺點

  1. 類數量膨脹:每增加一個具體實現就需要一個子類,可能導致類數量過多;
  2. 繼承局限性:C++ 不支持多繼承,子類若需復用多個模板流程會受限制;
  3. 耦合性:子類與父類的依賴較強,父類的修改可能影響所有子類。

五、C++ 中模板方法模式的應用場景

  1. 框架設計
    C++ 框架常通過模板方法模式定義核心流程,讓用戶通過子類定制細節。例如:

    • MFC 框架的Run()方法:定義了消息循環的骨架,用戶重寫OnDraw()等方法處理具體消息;
    • 測試框架(如 Google Test):Test類定義了測試用例的執行流程,用戶實現SetUp()TearDown()定制前后置操作。
  2. 業務流程標準化
    當多個業務場景共享相似流程但細節不同時,例如:

    • 支付流程(創建訂單→驗證→扣款→回調):不同支付方式(微信 / 支付寶)的 “扣款” 步驟不同;
    • 數據處理流程(讀取→解析→過濾→存儲):不同數據格式(JSON/XML)的 “解析” 步驟不同。
  3. 鉤子方法的靈活使用
    需要根據子類特性動態調整流程時,例如:

    • 日志框架:通過鉤子方法控制是否輸出調試日志;
    • 游戲 AI:通過鉤子方法決定角色在特定條件下的行為分支。

六、C++ 中模板方法模式的注意事項

  1. 模板方法用final修飾
    C++11 及以上支持final關鍵字,修飾模板方法可防止子類意外修改流程順序,例如:

    void prepareRecipe() final { ... } // 禁止子類重寫
    
  2. 抽象方法與鉤子方法的區分

    • 必須實現的步驟用純虛函數virtual void func() = 0);
    • 可選重寫的步驟用虛函數(提供默認實現),即鉤子方法。
  3. 避免過度設計
    若流程差異較小,不必強行使用模板方法模式,否則可能增加代碼復雜度。

七、總結

模板方法模式是 C++ 中實現 “流程復用 + 細節定制” 的經典模式,它通過抽象類定義算法骨架,用子類填充可變細節,既保證了流程的一致性,又保留了靈活性。

在 C++ 開發中,合理使用模板方法模式可以顯著減少重復代碼,提高系統的可維護性。但需注意平衡繼承帶來的耦合性,必要時可結合策略模式(通過組合實現)彌補繼承的局限性。

掌握模板方法模式,不僅能寫出更優雅的 C++ 代碼,更能理解許多 C++ 框架的設計思想 ——“骨架由框架定,細節由開發者填”

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

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

相關文章

LinkedList 深度解析:核心原理與實踐

文章目錄 一、底層數據結構與特性 1. 核心數據結構 2. 關鍵特性 二、核心操作機制解析 1. 添加元素機制 2. 刪除元素機制 三、性能關鍵點分析 1. 時間復雜度對比 2. 空間開銷 四、線程安全解決方案 1. 同步包裝器 2. 使用并發集合 五、經典面試題解析 1. ArrayList 和 LinkedLi…

Jmeter性能測試之安裝及啟動Jmeter

1. 安裝JDK Jmeter依賴JDK環境,如果電腦沒有JDK,需要安裝JDK.如下是Jmeter版本與JDK版本對應關系. 2. Jmeter下載安裝 下載鏈接&#xff1a;https://archive.apache.org/dist/jmeter/binaries/ windows下載.zip壓縮包Linux下載.tar壓縮包 下一步下一步就行 3. 配置環境變…

ShadowKV 機制深度解析:高吞吐長上下文 LLM 推理的 KV 緩存“影子”方案

背景與核心思想簡介 在LLM的長上下文推理中&#xff0c;KV Cache成為影響速度和內存的關鍵因素。每生成一個新token&#xff0c;模型需要對所有先前token的鍵&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;向量執行自注意力計算。傳統方法會將所有過去的K/V向量…

spring-ai整合PGVector實現RAG

背景 最近公司的產品和業務線&#xff0c;要求往ai方向靠攏&#xff0c;在研發各種智能體&#xff0c;整理下最近學習的過程&#xff0c;將一部分內容整理出來&#xff0c;分享給需要的同學。 這篇文章將會提供詳細的例子以及踩坑說明。主要內容是整合spring-ai&#xff0c;同…

Git 亂碼文件處理全流程指南

一、問題背景與核心目標 1.1 問題描述 在 Git 倉庫中發現了一個異常亂碼文件&#xff1a; "\001\342\240\025\250\325\3738\f\036\035\006\004\240\002\240\002\b\003\004\340\002\340\002\340\002\034\034\001\001\004:\016\020\001\005\016\016\016\211\266\257\211\266…

JavaScript垃圾回收機制

1.垃圾回收的概念 1.1 什么是垃圾回收機制&#xff1a; GC 即 Garbage Collection &#xff0c;程序工作過程中會產生很多"垃圾"&#xff0c;這些垃圾是程序不用的內存或者是之前用過了&#xff0c;以后不會再用的內存空間&#xff0c;而 GC 就是負責回收垃圾的&…

工業相機選擇規則

一、相機分辨率選擇相機分辨率指的是相機傳感器捕捉圖像細節的能力&#xff0c;具體來說就是傳感器上有效像素的總數量。可以把它理解為構成數字圖像的“小方塊”&#xff08;像素&#xff09;有多少個。工業領域內相機的分辨率的選擇根據更具產品需要的精度要求和產品大小來確…

【Web安全】csrf、ssrf和xxe的區別

CSRF、SSRF 和 XXE 是三種不同類型的網絡安全漏洞&#xff0c;它們的原理、攻擊目標、利用方式和危害場景均有顯著區別。以下從核心定義、原理、場景等維度詳細對比三者的差異。一、核心定義與原理對比漏洞類型全稱核心定義核心原理CSRF跨站請求偽造攻擊者誘導用戶在已登錄的情…

【Lua】XLua一鍵構建工具

將以下代碼放入Editor文件夾&#xff0c;點擊菜單欄的XLua/一鍵生成代碼和熱補丁 即可。using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEngine;/// <summary> /// XLua自動化構建工具 //…

20250808:EasyGBS 對接大華 ICC 平臺問題處理

最近有個現場在對接大華 ICC 平臺時&#xff0c;客戶反饋&#xff1a;EasyGBS 級聯成功&#xff0c;但 ICC 顯示下級離線。EasyGBS 成功對接過很多家國標平臺&#xff0c;但這種情況確實少見。我們遠程過去確認配置無誤后&#xff0c;就進行了抓包&#xff0c;拿到包我就納悶了…

js使用webscoket時使用自定義二進制包協議時并發問題處理

this.server new WebSocket.Server({ port: this.port });this.server.on(connection, (ws, req) > {const uniqueId dataUtil.uuid();ws.id uniqueId;global.serverSession.set(uniqueId, ws);logger.debug({ message: 客戶端已連接, traceId: ws.id, address: req.sock…

元數據管理與數據治理平臺:Apache Atlas 分類傳播 Classification Propagation

文中內容僅限技術學習與代碼實踐參考&#xff0c;市場存在不確定性&#xff0c;技術分析需謹慎驗證&#xff0c;不構成任何投資建議。Apache Atlas 框架是一套可擴展的核心基礎治理服務&#xff0c;使企業能夠有效、高效地滿足 Hadoop 中的合規性要求&#xff0c;并支持與整個企…

TSF應用開發與運維部署

架構演進歷程&#xff1a;單體架構-->SOA架構-->微服務架構-->Service Mesh騰訊微服務平臺TSF (Tencent Service Framework) 是一個圍繞應用和微服務的 PaaS 平臺。提供服務全生命周期管理能力和數據化運營支持。提供多維度應用、服務、機器的監控數據&#xff0c;助力…

linux開發之mmap內存映射

mmap概念 mmp是 將文件或設備直接映射到進程的虛擬內存空間 的一種機制&#xff0c;可實現程序像訪問內存一樣訪問文件&#xff0c;而不需要傳統的 read()/write()系統調用 文件內容被映射到進程的地址空間&#xff0c;讀寫文件就像操作內存一樣&#xff0c;操作系統負責自動同…

CPP繼承

繼承 一、繼承概述 1、為什么需要繼承 如下示例&#xff0c;Person 類、Student 類、Teacher 類有大量重復的代碼&#xff0c;造成代碼冗余&#xff0c;降低開發效率。我們可以通過繼承來解決這一問題。在面向對象的編程語言中&#xff0c;繼承是一個核心概念。主要作用將重復的…

模塊 PCB 技術在未來通信領域的創新突破方向

未來通信領域對數據傳輸速率、信號穩定性及設備集成度的要求持續攀升&#xff0c;模塊 PCB 作為通信設備的關鍵組件&#xff0c;其技術創新成為推動行業發展的核心動力。獵板 PCB 憑借深厚的技術積累與持續的研發投入&#xff0c;在模塊 PCB 技術創新方面取得諸多突破&#xff…

mysql的InnoDB索引總結

MySQL InnoDB索引知識點總結 1. 索引類型 1.1 聚簇索引&#xff08;Clustered Index&#xff09; 定義與特性 定義&#xff1a;聚簇索引是InnoDB的默認存儲方式&#xff0c;數據行按照主鍵的順序物理存儲在磁盤上特性&#xff1a; 每個InnoDB表只能有一個聚簇索引數據頁中的記錄…

C++模板的補充

類模板(上一篇沒講到類模板C/C內存管理&函數模板-CSDN博客&#xff09; 類模板的定義&#xff1a; template<class T1, class T2, ..., class Tn> class 類模板名 {// 類內成員定義 }; 用一個簡單的棧例子講類模板 #define _CRT_SECURE_NO_WARNINGS #include &l…

用JOIN替代子查詢的查詢性能優化

一、子查詢的性能瓶頸分析?重復執行成本?關聯子查詢會導致外層每行數據觸發一次子查詢&#xff0c;時間復雜度為O(M*N)sql-- 典型低效案例 SELECT e.employee_id, (SELECT d.department_name FROM departments d WHERE d.department_id e.department_id) FROM employees e; …

【設計模式】訪問者模式模式

訪問者模式&#xff08;Visitor Pattern&#xff09;詳解一、訪問者模式簡介 訪問者模式&#xff08;Visitor Pattern&#xff09; 是一種 行為型設計模式&#xff08;對象行為型模式&#xff09;&#xff0c;它允許你在不修改對象結構的前提下&#xff0c;為對象結構中的元素添…