设计模式:模板方法模式(Template Method)

概念

     模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,将某些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法中的某些特定步骤。

结构

模板方法模式包含以下主要角色:

  1. 抽象类(Abstract Class):定义抽象的原语操作(primitive operations),具体的子类将重定义它们以实现算法的特定步骤。实现一个模板方法,定义算法的骨架。

  2. 具体类(Concrete Class):实现原语操作以完成算法中与特定子类相关的步骤。

实现示例

#include <iostream>

// 抽象类
class AbstractClass {
public:
    // 模板方法,定义算法骨架
    void TemplateMethod() {
        PrimitiveOperation1();
        PrimitiveOperation2();
    }

    // 基本操作1 - 抽象方法,由子类实现
    virtual void PrimitiveOperation1() = 0;
    
    // 基本操作2 - 抽象方法,由子类实现
    virtual void PrimitiveOperation2() = 0;
    
    virtual ~AbstractClass() = default;
};

// 具体类A
class ConcreteClassA : public AbstractClass {
public:
    void PrimitiveOperation1() override {
        std::cout << "ConcreteClassA: PrimitiveOperation1 implementation\n";
    }
    
    void PrimitiveOperation2() override {
        std::cout << "ConcreteClassA: PrimitiveOperation2 implementation\n";
    }
};

// 具体类B
class ConcreteClassB : public AbstractClass {
public:
    void PrimitiveOperation1() override {
        std::cout << "ConcreteClassB: PrimitiveOperation1 implementation\n";
    }
    
    void PrimitiveOperation2() override {
        std::cout << "ConcreteClassB: PrimitiveOperation2 implementation\n";
    }
};

int main() {
    AbstractClass* a = new ConcreteClassA();
    a->TemplateMethod();
    
    AbstractClass* b = new ConcreteClassB();
    b->TemplateMethod();
    
    delete a;
    delete b;
    
    return 0;
}

输出结果

ConcreteClassA: PrimitiveOperation1 implementation
ConcreteClassA: PrimitiveOperation2 implementation
ConcreteClassB: PrimitiveOperation1 implementation
ConcreteClassB: PrimitiveOperation2 implementation

模式特点

  1. 代码复用:模板方法模式是一种代码复用的基本技术,将公共行为提取到父类中。

  2. 反向控制:父类调用子类的操作,而不是子类调用父类的操作(好莱坞原则:"Don't call us, we'll call you")。

  3. 灵活性:子类可以通过重写方法来实现特定的行为,而不需要改变算法结构。

应用场景

  1. 当多个类有相同的方法,且逻辑基本相同,只有一些细节不同时。

  2. 当需要控制子类扩展时,模板方法只在特定点调用"hook"操作,这样就只允许在这些点进行扩展。

  3. 当需要重构代码,将公共行为提取到父类中时。

优缺点

优点

  • 封装不变部分,扩展可变部分

  • 提取公共代码,便于维护

  • 行为由父类控制,子类实现

缺点

  • 每一个不同的实现都需要一个子类,导致类的个数增加,系统更加庞大

实际应用示例:游戏框架

#include <iostream>

// 游戏抽象类
class Game {
public:
    // 模板方法
    void Play() {
        Initialize();
        StartPlay();
        EndPlay();
    }
    
    virtual ~Game() = default;

protected:
    virtual void Initialize() = 0;
    virtual void StartPlay() = 0;
    virtual void EndPlay() = 0;
};

// 足球游戏
class Football : public Game {
protected:
    void Initialize() override {
        std::cout << "Football Game Initialized! Start playing.\n";
    }
    
    void StartPlay() override {
        std::cout << "Football Game Started. Enjoy the game!\n";
    }
    
    void EndPlay() override {
        std::cout << "Football Game Finished!\n";
    }
};

// 篮球游戏
class Basketball : public Game {
protected:
    void Initialize() override {
        std::cout << "Basketball Game Initialized! Start playing.\n";
    }
    
    void StartPlay() override {
        std::cout << "Basketball Game Started. Enjoy the game!\n";
    }
    
    void EndPlay() override {
        std::cout << "Basketball Game Finished!\n";
    }
};

int main() {
    Game* game = new Football();
    game->Play();
    delete game;
    
    game = new Basketball();
    game->Play();
    delete game;
    
    return 0;
}

通俗化总结

通俗理解:做菜的固定流程

想象你妈妈教你做一道菜,比如红烧肉,她告诉你固定的步骤框架:

  1. 准备食材(五花肉、调料等)

  2. 焯水去腥

  3. 炒糖色

  4. 炖煮收汁

这就是一个"模板方法"——固定的做菜流程。但具体到每一步:

  • 准备食材:你可以选择不同的五花肉部位

  • 焯水时间:根据肉块大小调整

  • 炒糖色:可以用白糖或冰糖,火候自己掌握

  • 炖煮时间:根据个人口味偏好调整

妈妈给了你做菜的"模板"(固定流程),但允许你在某些步骤上自由发挥(具体实现)。

其他生活例子

  1. 咖啡/茶制作

    • 模板方法:①烧水 ②加原料 ③冲泡 ④加调料

    • 具体实现:咖啡用咖啡粉,茶用茶叶;咖啡加糖奶,茶可能加柠檬

  2. 银行办业务

    • 模板流程:①取号 ②等待 ③办理 ④评价

    • 不同业务(存款/开户/转账)在第③步具体实现不同

  3. 组装电脑

    • 固定流程:①装CPU ②装内存 ③装硬盘 ④接线

    • 具体实现:可以选择不同品牌型号的配件

模式精髓总结

固定骨架:定义不变的流程步骤(比如做菜必须按顺序来,不能先炖煮再焯水)

灵活细节:允许子类自定义某些步骤的具体实现(比如糖色用冰糖还是白糖)

控制权在父类:就像妈妈控制做菜流程,你只需要负责具体操作

这种模式特别适用于:

  • 有固定流程但细节可变的情况

  • 希望避免重复代码(多个子类共享相同流程)

  • 需要控制子类扩展点的情况

就像各种菜系都有"先炒后炖"的固定套路,但具体炒什么、炖什么可以千变万化!

实际C++项目开发中的示例

1. 文档导出框架示例

例如需要开发一个支持多种格式的文档导出功能(PDF、HTML、Markdown)

#include <iostream>
#include <string>
#include <vector>

// 抽象文档导出类
class DocumentExporter {
public:
    // 模板方法
    void ExportDocument(const std::string& title, const std::vector<std::string>& content) {
        CreateHeader(title);
        for (const auto& paragraph : content) {
            AddParagraph(paragraph);
        }
        CreateFooter();
        Finalize();
    }

    virtual ~DocumentExporter() = default;

protected:
    virtual void CreateHeader(const std::string& title) = 0;
    virtual void AddParagraph(const std::string& text) = 0;
    virtual void CreateFooter() = 0;
    virtual void Finalize() {
        // 默认实现,子类可重写
        std::cout << "Export completed!\n";
    }
};

// PDF导出实现
class PDFExporter : public DocumentExporter {
protected:
    void CreateHeader(const std::string& title) override {
        std::cout << "Creating PDF header with title: " << title << "\n";
    }

    void AddParagraph(const std::string& text) override {
        std::cout << "Adding PDF paragraph: " << text << "\n";
    }

    void CreateFooter() override {
        std::cout << "Adding PDF footer with page number\n";
    }

    void Finalize() override {
        std::cout << "Generating PDF file...\n";
        DocumentExporter::Finalize();
    }
};

// HTML导出实现
class HTMLExporter : public DocumentExporter {
protected:
    void CreateHeader(const std::string& title) override {
        std::cout << "<html><head><title>" << title << "</title></head><body>\n";
    }

    void AddParagraph(const std::string& text) override {
        std::cout << "<p>" << text << "</p>\n";
    }

    void CreateFooter() override {
        std::cout << "</body></html>\n";
    }
};

// 使用示例
int main() {
    std::vector<std::string> content = {
        "This is the first paragraph.",
        "This is the second paragraph.",
        "Here goes the third one."
    };

    DocumentExporter* pdfExporter = new PDFExporter();
    pdfExporter->ExportDocument("Sample Document", content);
    delete pdfExporter;

    std::cout << "\n";

    DocumentExporter* htmlExporter = new HTMLExporter();
    htmlExporter->ExportDocument("Sample Document", content);
    delete htmlExporter;

    return 0;
}

2. 游戏引擎中的场景加载系统
 

#include <iostream>
#include <memory>

// 游戏场景加载基类
class SceneLoader {
public:
    // 模板方法
    void LoadScene() {
        PreloadResources();
        LoadMainAssets();
        InitializeSceneObjects();
        PostInitialization();
        if (EnableDebug()) {
            AttachDebugTools();
        }
    }

    virtual ~SceneLoader() = default;

protected:
    virtual void PreloadResources() = 0;
    virtual void LoadMainAssets() = 0;
    virtual void InitializeSceneObjects() = 0;
    
    // 钩子方法(Hook) - 可选步骤
    virtual void PostInitialization() {
        // 默认空实现
    }
    
    virtual bool EnableDebug() const {
        return false; // 默认不启用调试
    }
    
    virtual void AttachDebugTools() {
        std::cout << "Attaching default debug tools...\n";
    }
};

// 主菜单场景加载
class MainMenuLoader : public SceneLoader {
protected:
    void PreloadResources() override {
        std::cout << "Preloading menu textures and fonts...\n";
    }

    void LoadMainAssets() override {
        std::cout << "Loading menu background and buttons...\n";
    }

    void InitializeSceneObjects() override {
        std::cout << "Initializing menu UI elements...\n";
    }
};

// 战斗场景加载
class BattleSceneLoader : public SceneLoader {
protected:
    void PreloadResources() override {
        std::cout << "Preloading battle assets (models, sounds)...\n";
    }

    void LoadMainAssets() override {
        std::cout << "Loading map, characters and enemies...\n";
    }

    void InitializeSceneObjects() override {
        std::cout << "Placing characters, setting up AI...\n";
    }

    void PostInitialization() override {
        std::cout << "Starting background music for battle...\n";
    }

    bool EnableDebug() const override {
        return true; // 战斗场景启用调试
    }

    void AttachDebugTools() override {
        std::cout << "Attaching battle debug tools (HP bars, collision visualizer)...\n";
    }
};

int main() {
    std::unique_ptr<SceneLoader> menuLoader = std::make_unique<MainMenuLoader>();
    menuLoader->LoadScene();
    
    std::cout << "\n";
    
    std::unique_ptr<SceneLoader> battleLoader = std::make_unique<BattleSceneLoader>();
    battleLoader->LoadScene();
    
    return 0;
}

3. 网络通信协议处理框架

#include <iostream>
#include <vector>

// 协议处理器基类
class ProtocolHandler {
public:
    // 模板方法
    void ProcessData(const std::vector<uint8_t>& rawData) {
        if (!ValidateData(rawData)) {
            HandleInvalidData();
            return;
        }
        
        auto parsed = ParseData(rawData);
        ProcessParsedData(parsed);
        SendResponse();
        
        if (NeedLogging()) {
            LogTransaction(rawData);
        }
    }

    virtual ~ProtocolHandler() = default;

protected:
    virtual bool ValidateData(const std::vector<uint8_t>& data) = 0;
    virtual std::string ParseData(const std::vector<uint8_t>& data) = 0;
    virtual void ProcessParsedData(const std::string& parsedData) = 0;
    virtual void SendResponse() {
        std::cout << "Sending default response...\n";
    }
    
    virtual void HandleInvalidData() {
        std::cerr << "Error: Invalid data received!\n";
    }
    
    // 钩子方法
    virtual bool NeedLogging() const { return false; }
    virtual void LogTransaction(const std::vector<uint8_t>& data) {
        std::cout << "Logging transaction data...\n";
    }
};

// HTTP协议处理器
class HttpProtocolHandler : public ProtocolHandler {
protected:
    bool ValidateData(const std::vector<uint8_t>& data) override {
        std::cout << "Validating HTTP headers...\n";
        return data.size() > 10; // 简单示例验证
    }

    std::string ParseData(const std::vector<uint8_t>& data) override {
        std::cout << "Parsing HTTP request...\n";
        return "Parsed HTTP request";
    }

    void ProcessParsedData(const std::string& parsedData) override {
        std::cout << "Processing HTTP request: " << parsedData << "\n";
    }

    void SendResponse() override {
        std::cout << "Sending HTTP 200 OK response\n";
    }

    bool NeedLogging() const override { return true; }
};

// FTP协议处理器
class FtpProtocolHandler : public ProtocolHandler {
protected:
    bool ValidateData(const std::vector<uint8_t>& data) override {
        std::cout << "Validating FTP command...\n";
        return !data.empty();
    }

    std::string ParseData(const std::vector<uint8_t>& data) override {
        std::cout << "Parsing FTP command...\n";
        return "Parsed FTP command";
    }

    void ProcessParsedData(const std::string& parsedData) override {
        std::cout << "Executing FTP command: " << parsedData << "\n";
    }
};

int main() {
    std::vector<uint8_t> httpData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
    std::vector<uint8_t> ftpData = {0x41, 0x42, 0x43};
    
    ProtocolHandler* httpHandler = new HttpProtocolHandler();
    httpHandler->ProcessData(httpData);
    delete httpHandler;
    
    std::cout << "\n";
    
    ProtocolHandler* ftpHandler = new FtpProtocolHandler();
    ftpHandler->ProcessData(ftpData);
    delete ftpHandler;
    
    return 0;
}

开发中的关键点

  1. 识别固定流程:找出项目中那些有固定步骤但某些步骤需要变化的部分

  2. 设计钩子方法

    • 抽象方法:必须由子类实现

    • 空方法:子类可选择实现

    • 带默认实现的方法:子类可重写

  3. 控制反转:让父类控制流程,子类只负责具体实现

  4. 应用场景

    • 框架设计(如游戏引擎、网络库)

    • 标准化流程(如文档导出、数据导入)

    • 算法骨架固定但部分步骤可变的情况

这些示例展示了模板方法模式如何在实际项目中提供清晰的框架,同时保持足够的灵活性来处理各种具体实现。

     模板方法模式是一种非常实用的设计模式,特别适用于框架设计,它定义了框架的流程,而将具体的实现细节留给子类去完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值