網站的開發(fā)方法谷歌全球營銷
模板方法模式是面向對象軟件設計模式之一,其主要意圖是在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中實現(xiàn)。模板方法使得子類可以在不改變算法結構的情況下重新定義算法的某些特定步驟。
動機
在軟件開發(fā)中,常常會遇到這樣的情況:一個任務可以被分解為多個步驟來完成,其中有些步驟的實現(xiàn)方法是對所有子類通用的,而有些步驟則需要針對不同的子類有不同的實現(xiàn)方式。模板方法模式正是解決這一問題的有效手段。通過使用模板方法模式,可以將不變的部分代碼抽離出來放在基類中,而將可變的部分留給子類去實現(xiàn)。這樣不僅減少了代碼的重復,而且還能夠保證變與不變的部分之間的聯(lián)系。
意圖
模板方法模式的主要意圖是定義一個操作中的算法的骨架,而將一些步驟的實現(xiàn)延遲到子類中。這樣做可以讓子類在不改變算法整體結構的前提下去重寫算法的某些特定部分。
適用場合
- 不變的算法骨架:當某個抽象類有多個子類,但是它們有共同的算法步驟,只是某些步驟的具體實現(xiàn)不同,可以通過模版方法模式將這些不變的部分提取到抽象類中,子類只需要實現(xiàn)特定的步驟。
- 避免代碼重復:多個類中若有一段代碼完全相同或相似,可以考慮使用模版方法模式來抽取共享的部分,減少代碼重復。
- 控制子類擴展:模版方法模式可以通過在模版方法中調用“鉤子”方法,來控制子類在生命周期中的特定點上能夠做什么,不能夠做什么。鉤子方法在抽象類中通常是空實現(xiàn),子類可以覆蓋這些方法來實現(xiàn)特定功能。
示例
模板方法模式的一個經典示例是咖啡店和茶館的飲料制作過程。制作咖啡和茶的基本流程大致相同(準備熱水、沖泡、倒入杯子、加調料),但具體的步驟(如沖泡的方式、加何種調料)則不同。通過定義一個模版方法來實現(xiàn)這個流程,基類可以控制整個流程的執(zhí)行順序,而具體的沖泡和加調料動作則由子類實現(xiàn)。
模板方法模式是一種非常有用的設計模式,在很多框架和庫中都有應用。正確地使用模版方法模式可以使代碼更加清晰、易于擴展和維護。
面是一個簡單的C++示例,展示了如何使用模板方法模式來實現(xiàn)咖啡和茶的制作過程。在這個例子中,基類?Beverage
?定義了一個模板方法?prepareRecipe()
,該方法包含了制作飲料的通用步驟,而具體的步驟(如沖泡和添加調料)則由子類實現(xiàn)。
代碼示例
#include <iostream>
using namespace std;// 基類:Beverage
class Beverage {
public:// 模板方法,定義了制作飲料的算法骨架void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}// 子類不需要覆蓋的方法,因為這些步驟對所有飲料都是相同的void boilWater() {cout << "Boiling water" << endl;}void pourInCup() {cout << "Pouring into cup" << endl;}// 子類必須覆蓋的方法,因為這些步驟對不同飲料是不同的virtual void brew() = 0; // 純虛函數,必須在子類中實現(xiàn)virtual void addCondiments() = 0; // 純虛函數,必須在子類中實現(xiàn)
};// 子類:Coffee
class Coffee : public Beverage {
public:void brew() override {cout << "Dripping Coffee through filter" << endl;}void addCondiments() override {cout << "Adding Sugar and Milk" << endl;}
};// 子類:Tea
class Tea : public Beverage {
public:void brew() override {cout << "Steeping the tea" << endl;}void addCondiments() override {cout << "Adding Lemon" << endl;}
};int main() {cout << "Making Coffee..." << endl;Coffee coffee;coffee.prepareRecipe();cout << "\nMaking Tea..." << endl;Tea tea;tea.prepareRecipe();return 0;
}
運行結果
Making Coffee...
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Sugar and MilkMaking Tea...
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon
解釋
-
基類?
Beverage
:prepareRecipe()
?是模板方法,定義了制作飲料的步驟:煮水、沖泡、倒入杯子、加調料。boilWater()
?和?pourInCup()
?是所有飲料共有的步驟,因此在基類中實現(xiàn)。brew()
?和?addCondiments()
?是純虛函數,需要在子類中實現(xiàn),因為這些步驟對不同的飲料有不同的實現(xiàn)。
-
子類?
Coffee
?和?Tea
:Coffee
?子類實現(xiàn)了?brew()
?和?addCondiments()
,分別表示沖泡咖啡和添加糖和牛奶。Tea
?子類實現(xiàn)了?brew()
?和?addCondiments()
,分別表示泡茶和添加檸檬。
-
main
?函數:- 創(chuàng)建?
Coffee
?和?Tea
?對象,并調用它們的?prepareRecipe()
?方法,輸出了制作咖啡和茶的過程。
- 創(chuàng)建?
適用場合
- 通用流程,具體步驟不同:當多個子類需要遵循相同的流程,但具體的步驟實現(xiàn)不同,可以使用模板方法模式。例如,不同的支付方式(信用卡、支付寶、微信)可以遵循相同的支付流程,但具體的支付接口和驗證方式不同。
- 避免重復代碼:在多個子類中存在相同的邏輯時,可以將這些邏輯提取到基類的模板方法中,避免代碼重復。
模板方法模式通過將算法的結構固定,允許子類靈活地實現(xiàn)具體步驟,提供了一種優(yōu)雅的方式來處理類似的任務。
?模板方法模式經常與其他設計模式協(xié)同使用,以解決更復雜的設計問題。常見的協(xié)同模式包括策略模式、工廠方法模式和狀態(tài)模式等。下面我們將重點介紹模板方法模式與策略模式的協(xié)同使用,并給出一個C++代碼示例。
模板方法模式與策略模式的協(xié)同使用
動機
模板方法模式用于定義一個算法的骨架,而將某些步驟的實現(xiàn)延遲到子類中。策略模式用于定義一系列可互換的算法,并將這些算法封裝在獨立的類中。通過將策略模式與模板方法模式結合,可以在一個模板方法中使用不同的策略,從而提供更大的靈活性。
適用場景
- 算法的某些步驟需要根據不同的條件動態(tài)選擇不同的實現(xiàn):可以通過策略模式將這些步驟的實現(xiàn)封裝在不同的策略類中,然后在模板方法中根據需要選擇合適的策略。
- 需要在運行時動態(tài)改變算法的某些步驟:策略模式允許在運行時動態(tài)更換算法,而模板方法模式確保了算法的整體結構不變。
代碼示例
假設我們有一個任務是處理數據,不同的處理策略可以根據不同的需求進行選擇。我們使用模板方法模式來定義數據處理的骨架,使用策略模式來實現(xiàn)不同的處理策略。
1. 定義策略接口和具體策略
// 策略接口
class DataProcessingStrategy {
public:virtual void process() = 0;virtual ~DataProcessingStrategy() = default;
};// 具體策略1:壓縮數據
class CompressStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Compressing data" << endl;}
};// 具體策略2:加密數據
class EncryptStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Encrypting data" << endl;}
};// 具體策略3:校驗數據
class VerifyStrategy : public DataProcessingStrategy {
public:void process() override {cout << "Verifying data" << endl;}
};
2. 定義抽象類和模板方法
// 抽象類
class DataProcessor {
protected:DataProcessingStrategy* strategy;public:DataProcessor(DataProcessingStrategy* s) : strategy(s) {}virtual ~DataProcessor() {delete strategy;}// 模板方法void processData() {load();strategy->process();save();}virtual void load() {cout << "Loading data" << endl;}virtual void save() {cout << "Saving data" << endl;}
};
3. 定義具體的數據處理器
// 具體的數據處理器1:使用壓縮策略
class CompressDataProcessor : public DataProcessor {
public:CompressDataProcessor() : DataProcessor(new CompressStrategy()) {}
};// 具體的數據處理器2:使用加密策略
class EncryptDataProcessor : public DataProcessor {
public:EncryptDataProcessor() : DataProcessor(new EncryptStrategy()) {}
};// 具體的數據處理器3:使用校驗策略
class VerifyDataProcessor : public DataProcessor {
public:VerifyDataProcessor() : DataProcessor(new VerifyStrategy()) {}
};
4. 客戶端代碼
#include <iostream>
using namespace std;int main() {// 使用壓縮策略處理數據cout << "Using Compress Strategy..." << endl;DataProcessor* processor1 = new CompressDataProcessor();processor1->processData();delete processor1;// 使用加密策略處理數據cout << "\nUsing Encrypt Strategy..." << endl;DataProcessor* processor2 = new EncryptDataProcessor();processor2->processData();delete processor2;// 使用校驗策略處理數據cout << "\nUsing Verify Strategy..." << endl;DataProcessor* processor3 = new VerifyDataProcessor();processor3->processData();delete processor3;return 0;
}
運行結果
Using Compress Strategy...
Loading data
Compressing data
Saving dataUsing Encrypt Strategy...
Loading data
Encrypting data
Saving dataUsing Verify Strategy...
Loading data
Verifying data
Saving data
解釋
-
策略接口?
DataProcessingStrategy
:- 定義了一個純虛函數?
process()
,用于實現(xiàn)數據處理的具體策略。 - 具體策略?
CompressStrategy
、EncryptStrategy
?和?VerifyStrategy
?分別實現(xiàn)了數據壓縮、加密和校驗的策略。
- 定義了一個純虛函數?
-
抽象類?
DataProcessor
:- 包含一個策略對象?
strategy
,并通過構造函數傳遞具體的策略對象。 - 定義了模板方法?
processData()
,該方法調用了?load()
、strategy->process()
?和?save()
,確保數據處理的流程一致。 load()
?和?save()
?是通用的數據加載和保存步驟,可以在子類中根據需要進行擴展。
- 包含一個策略對象?
-
具體數據處理器類:
CompressDataProcessor
、EncryptDataProcessor
?和?VerifyDataProcessor
?分別使用不同的策略對象初始化?DataProcessor
。
-
客戶端代碼:
- 創(chuàng)建不同的數據處理器對象,并調用?
processData()
?方法來處理數據,輸出了不同的處理策略的結果。
- 創(chuàng)建不同的數據處理器對象,并調用?
通過這種方式,模板方法模式和策略模式的結合提供了更大的靈活性,允許在運行時動態(tài)選擇不同的數據處理策略,同時保持數據處理流程的一致性。