首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

设计模式-模板方法模式-Template Method Pattern

  • 25-02-19 08:20
  • 3369
  • 10764
blog.csdn.net

系列文章目录

总目录链接


文章目录

  • 系列文章目录
    • 总目录链接
  • 设计模式-模板方法模式-Template Method Pattern
    • Overview
    • 1.模板方法模式(Template Method Pattern)
      • 1.1.特点
      • 1.2.结构
      • 1.3.示例代码
        • 1.3.1.定义模板基类
        • 1.3.2.实现具体步骤
        • 1.3.3.客户端代码
        • 1.3.4.编译和运行
        • 1.3.5.输出
      • 1.4.应用场景
      • 1.5.优点
      • 1.6.缺点
    • 2.模板方法模式优缺点
    • 3.模板方法模式 应用场景
    • 4.如何用模板方法模式解决实际开发中的特定问题?
      • 4.1.示例:报表生成器
      • 4.2.输出
    • 5.在实际开发中,如何确定一个算法是否适合使用模板方法模式?
    • 6.如何在我的项目中实现模板方法模式?
      • 6.1. 确定适用场景
      • 6.2. 定义抽象基类
      • 6.3. 实现具体步骤
      • 6.4. 创建具体子类
      • 6.5. 使用多态
        • 6.5.1.示例实现
      • 6.6. 测试和验证
      • 6.7. 文档和维护
    • 关于作者


设计模式-模板方法模式-Template Method Pattern

Overview

  • 模板方法模式(Template Method Pattern)是一种行为设计模式
  • 它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重写算法的某些步骤
  • 这种模式是通过在抽象类中定义一个模板方法来实现的,这个模板方法调用多个其他的方法,这些方法可以是抽象的,可以在子类中得到进一步实现

1.模板方法模式(Template Method Pattern)

模板方法模式(Template Method Pattern)是一种行为设计模式,它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重写算法的某些步骤。这种模式是通过在抽象类中定义一个模板方法来实现的,这个模板方法调用多个其他的方法,这些方法可以是抽象的,可以在子类中得到进一步实现。

1.1.特点

  1. 算法框架:在抽象类中定义算法的步骤。
  2. 扩展性:子类可以重写算法的某些步骤,而不需要改变算法的结构。
  3. 代码复用:通过模板方法,可以在不同的子类中复用算法框架。

1.2.结构

  • AbstractClass:定义模板方法和算法框架。
  • ConcreteClass:实现在抽象类中定义的抽象方法。

1.3.示例代码

在C++中实现模板方法模式,我们通常结合模板类和虚函数来创建一个通用的算法框架,同时允许子类根据需要重写某些步骤。下面是一个使用模板类实现模板方法模式的示例:

1.3.1.定义模板基类

首先,我们定义一个模板基类,其中包含模板方法和一些虚函数,这些虚函数将在派生类中被重写。

#include 
#include 

// 模板基类
template <typename T>
class Algorithm {
public:
    // 模板方法
    void execute() {
        step1();
        step2();
        step3();
    }

protected:
    // 抽象步骤,子类必须实现
    virtual void step1() = 0;
    virtual void step2() = 0;
    virtual void step3() = 0;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
1.3.2.实现具体步骤

接下来,我们创建一个派生类,实现基类中定义的抽象步骤。

// 具体算法类
template <typename T>
class ConcreteAlgorithm : public Algorithm<T> {
public:
    void step1() override {
        std::cout << "ConcreteAlgorithm: Step 1" << std::endl;
    }

    void step2() override {
        std::cout << "ConcreteAlgorithm: Step 2" << std::endl;
    }

    void step3() override {
        std::cout << "ConcreteAlgorithm: Step 3" << std::endl;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
1.3.3.客户端代码

最后,我们编写客户端代码来使用这些类。

int main() {
    ConcreteAlgorithm<int> algorithm;
    algorithm.execute(); // 执行算法

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
1.3.4.编译和运行

将上述代码保存到一个.cpp文件中,然后使用C++编译器编译并运行。例如,如果你使用g++,可以使用以下命令:

g++ -o algorithm algorithm.cpp
./algorithm
  • 1
  • 2
1.3.5.输出

运行程序后,你将看到以下输出:

ConcreteAlgorithm: Step 1
ConcreteAlgorithm: Step 2
ConcreteAlgorithm: Step 3
  • 1
  • 2
  • 3

这个示例展示了如何在C++中使用模板类来实现模板方法模式。这种模式允许你在不改变算法结构的情况下,通过子类重写算法的某些步骤来实现多态性。这种模式在实际开发中非常有用,特别是在需要实现多种相似算法或行为时。

1.4.应用场景

  1. 多个子类有共同的方法:这些方法的实现步骤相同,但具体实现不同。
  2. 需要控制扩展:当算法的结构不应改变,但需要在不同的子类中有不同的行为时。

1.5.优点

  • 代码复用:减少代码重复。
  • 扩展性:子类可以扩展算法的某些步骤。
  • 控制反转:算法的控制权在父类,实现在子类。

1.6.缺点

  • 有时可导致设计更加复杂:如果算法的步骤很多,可能会导致设计变得复杂。

模板方法模式是一个很好的工具,用于在保持算法结构不变的情况下,允许子类有选择地实现算法的某些步骤。

2.模板方法模式优缺点

  • 优点
    • 你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
    • 你可将重复代码提取到一个超类中。
  • 缺点
    • 部分客户端可能会受到算法框架的限制。
    • 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。
    • 模板方法中的步骤越多, 其维护工作就可能会越困难。

3.模板方法模式 应用场景

模板方法模式在C++中的使用场景通常涉及到需要在子类中实现算法的某些步骤,同时保持算法的总体结构不变。以下是一些典型的应用场景:

  1. 算法框架:

    • 当你需要在不同的子类中实现相似的算法时,你可以在基类中定义算法的骨架,然后在每个子类中实现具体的步骤。
  2. 数据处理:

    • 在处理数据时,可能需要执行一系列固定的步骤,如验证、处理和保存数据。模板方法模式可以定义这些步骤的顺序,而具体的处理逻辑则由子类实现。
  3. 用户界面操作:

    • 用户界面操作通常遵循一定的模式,如初始化界面、处理用户输入、更新界面和清理资源。模板方法模式可以用来定义这些步骤,而具体的界面元素和事件处理则由子类实现。
  4. 游戏开发:

    • 在游戏开发中,不同类型的游戏可能需要遵循相似的游戏循环,如初始化游戏、开始游戏、处理游戏逻辑和结束游戏。模板方法模式可以用来定义这些步骤,而具体的游戏逻辑则由子类实现。
  5. 数据库访问:

    • 数据库访问通常包括连接数据库、执行查询、处理结果和关闭连接等步骤。模板方法模式可以用来定义这些步骤,而具体的数据库操作则由子类实现。
  6. 设计模式实现:

    • 许多设计模式,如建造者模式、工厂方法模式等,可以通过模板方法模式来实现,以确保在创建复杂对象时遵循一定的步骤。
  7. 测试框架:

    • 在测试框架中,测试过程可能包括设置测试环境、执行测试用例、验证结果和清理测试环境等步骤。模板方法模式可以用来定义这些步骤,而具体的测试逻辑则由测试用例类实现。
  8. 插件架构:

    • 在插件架构中,插件可能需要遵循一定的生命周期,如初始化、激活、执行和停用。模板方法模式可以用来定义这些生命周期的执行顺序,而具体的插件逻辑则由子类实现。
  9. 报表生成:

    • 报表生成通常包括数据收集、数据过滤、数据排序和生成报表等步骤。模板方法模式可以用来定义这些步骤的执行顺序,而具体的报表格式和数据源则由子类实现。
  10. 文件处理:

    • 在处理不同类型文件时,可能需要执行一系列相似的步骤,如打开文件、读取内容、处理数据和关闭文件。模板方法模式可以用来定义这些步骤,而具体的文件格式处理则由子类实现。

在实际开发中,模板方法模式有助于减少代码重复,提高代码的可维护性和可扩展性。通过定义一个通用的算法框架,你可以在不同的上下文中重用这个框架,同时允许子类提供特定的实现细节。

4.如何用模板方法模式解决实际开发中的特定问题?

使用模板方法模式解决实际开发中的特定问题通常涉及以下步骤:

  1. 识别算法框架:

    • 确定问题域中的通用算法框架,即一系列步骤,这些步骤在不同的场景中都是相似的,但每个步骤的具体实现可能有所不同。
  2. 定义抽象基类:

    • 创建一个抽象基类,其中包含模板方法和一系列抽象步骤。模板方法是算法框架的实现,它调用这些抽象步骤。
  3. 实现具体步骤:

    • 为每个步骤提供默认实现(如果可能),或者声明它们为纯虚函数,要求子类提供具体实现。
  4. 创建具体子类:

    • 为每种特定场景创建一个子类,重写基类中的抽象步骤,以提供特定于场景的实现。
  5. 使用算法:

    • 在客户端代码中,使用基类指针或引用来操作子类对象,利用多态性执行模板方法。

4.1.示例:报表生成器

假设我们需要开发一个报表生成器,它可以生成不同类型的报表,但所有报表生成过程都遵循相同的步骤:收集数据、处理数据、格式化输出。

  • 步骤 1:定义抽象基类
#include 
#include 
#include 

// 抽象基类
class Report {
public:
    // 模板方法
    virtual void generate() {
        collectData();
        processData();
        outputData();
    }

protected:
    virtual void collectData() = 0;
    virtual void processData() = 0;
    void outputData() {
        std::cout << "Outputting data..." << std::endl;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 步骤 2:创建具体子类
// 具体子类:财务报表
class FinancialReport : public Report {
protected:
    void collectData() override {
        std::cout << "Collecting financial data..." << std::endl;
    }

    void processData() override {
        std::cout << "Processing financial data..." << std::endl;
    }
};

// 具体子类:销售报表
class SalesReport : public Report {
protected:
    void collectData() override {
        std::cout << "Collecting sales data..." << std::endl;
    }

    void processData() override {
        std::cout << "Processing sales data..." << std::endl;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 步骤 3:使用算法
int main() {
    std::unique_ptr<Report> report;

    report = std::make_unique<FinancialReport>();
    report->generate();

    report = std::make_unique<SalesReport>();
    report->generate();

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.2.输出

Collecting financial data...
Processing financial data...
Outputting data...
Collecting sales data...
Processing sales data...
Outputting data...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这个例子中,Report 类定义了报表生成的模板方法,包括收集数据、处理数据和输出数据的步骤。FinancialReport 和 SalesReport 类分别实现了这些步骤的具体逻辑。客户端代码通过基类指针来操作不同的报表对象,实现了多态性。

通过这种方式,模板方法模式帮助我们定义了一个通用的算法框架,同时允许在不同的子类中实现特定的步骤,使得代码更加模块化和可扩展。

5.在实际开发中,如何确定一个算法是否适合使用模板方法模式?

在实际开发中,确定一个算法是否适合使用模板方法模式,可以通过以下几个关键点来评估:

  1. 算法的稳定性:

    • 算法的总体结构是否稳定,而只有某些步骤的具体实现会变化。如果算法的核心步骤在不同情况下保持不变,那么它可能适合使用模板方法模式。
  2. 步骤的多样性:

    • 算法是否包含多个步骤,其中一些步骤在不同情况下有不同的实现。模板方法模式允许在基类中定义算法框架,而将具体实现留给子类。
  3. 多态性的需求:

    • 是否需要通过多态性来处理不同类型的对象。模板方法模式通过在基类中定义模板方法和在子类中重写步骤,可以实现多态性。
  4. 代码复用:

    • 是否希望在不同的子类中复用算法框架。如果算法的某些步骤在多个子类中是通用的,那么使用模板方法模式可以减少代码重复。
  5. 扩展性:

    • 是否需要在未来可能添加新的步骤或子类。模板方法模式提供了良好的扩展性,允许在不修改现有代码的情况下添加新的步骤或子类。
  6. 控制反转:

    • 是否希望在基类中控制算法的流程,而在子类中提供具体实现。模板方法模式将算法的控制权集中在基类,而将实现细节留给子类。
  7. 复杂性管理:

    • 算法是否足够复杂,以至于将其分解为多个步骤可以提高代码的可读性和可维护性。模板方法模式可以帮助管理复杂性,通过将算法分解为更小的、可管理的步骤。
  8. 客户端代码的简洁性:

    • 是否希望客户端代码能够简洁地使用算法,而不需要关心具体的实现细节。模板方法模式允许客户端通过基类接口来使用算法,而具体的实现细节由子类处理。
  9. 设计原则的遵循:

    • 是否符合开闭原则(对扩展开放,对修改封闭)。模板方法模式允许在不修改现有代码的情况下扩展新的步骤或子类,符合开闭原则。
  10. 上下文环境:

    • 考虑项目的具体上下文,例如团队的熟悉度、项目的时间线、维护成本等。如果团队对模板方法模式熟悉,且项目允许一定程度的设计工作,那么使用模板方法模式可能是合适的。

通过考虑这些因素,你可以决定一个算法是否适合使用模板方法模式。如果算法符合上述多数条件,那么使用模板方法模式可能是一个合适的选择,它可以帮助提高代码的可维护性、可扩展性和复用性。

6.如何在我的项目中实现模板方法模式?

在你的项目中实现模板方法模式,可以遵循以下步骤:

6.1. 确定适用场景

首先,确保你的场景适合使用模板方法模式。这通常意味着你有一个算法框架,它在多个类中是通用的,但某些步骤需要在子类中定制。

6.2. 定义抽象基类

创建一个抽象基类,它定义了模板方法和算法的框架。这个类将包含:

  • 模板方法:一个或多个步骤组成的算法,这些步骤通过调用抽象操作来执行。
  • 抽象操作:定义为纯虚函数(pure virtual functions),子类必须实现这些函数。

6.3. 实现具体步骤

在抽象基类中实现一些步骤,这些步骤可以是具体的,也可以是抽象的。具体的步骤可以直接提供实现,而抽象步骤则留给子类实现。

6.4. 创建具体子类

为每个需要定制算法步骤的场景创建子类。这些子类将实现抽象基类中定义的抽象步骤。

6.5. 使用多态

在客户端代码中,使用抽象基类的指针或引用来操作子类对象。这样,你可以利用多态性在运行时调用正确的子类实现。

6.5.1.示例实现

假设你正在开发一个支付系统,其中不同的支付方式(如信用卡、PayPal、银行转账)需要执行相似的步骤,但每个步骤的具体实现不同。

  • 步骤 1:定义抽象基类
class PaymentProcessor {
public:
    // 模板方法
    void processPayment() {
        authorizePayment();
        capturePayment();
        finalizePayment();
    }

protected:
    // 抽象步骤
    virtual void authorizePayment() = 0;
    virtual void capturePayment() = 0;
    virtual void finalizePayment() = 0;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 步骤 2:创建具体子类
class CreditCardProcessor : public PaymentProcessor {
protected:
    void authorizePayment() override {
        std::cout << "Authorizing credit card payment." << std::endl;
    }

    void capturePayment() override {
        std::cout << "Capturing credit card payment." << std::endl;
    }

    void finalizePayment() override {
        std::cout << "Finalizing credit card payment." << std::endl;
    }
};

class PayPalProcessor : public PaymentProcessor {
protected:
    void authorizePayment() override {
        std::cout << "Authorizing PayPal payment." << std::endl;
    }

    void capturePayment() override {
        std::cout << "Capturing PayPal payment." << std::endl;
    }

    void finalizePayment() override {
        std::cout << "Finalizing PayPal payment." << std::endl;
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 步骤 3:使用多态
int main() {
    PaymentProcessor* processor = new CreditCardProcessor();
    processor->processPayment();

    delete processor; // 清理资源

    processor = new PayPalProcessor();
    processor->processPayment();

    delete processor; // 清理资源

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6.6. 测试和验证

确保你的实现符合预期,通过单元测试验证每个子类的正确性,以及模板方法模式的整体行为。

6.7. 文档和维护

为你的代码编写文档,说明模板方法模式的使用和每个类的职责。随着项目的进展,维护和扩展你的模板方法实现。

通过这些步骤,你可以在你的项目中有效地实现模板方法模式,提高代码的可重用性和可维护性。


关于作者

  • 微信公众号:WeSiGJ
  • GitHub:https://github.com/wesigj/cplusplusboys
  • CSDN:https://blog.csdn.net/wesigj
  • 微博:
  • -版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
WeSiGJ
微信公众号
共同分享,共同交流, 共同学习!
注:本文转载自blog.csdn.net的WeSiGJ的文章"https://blog.csdn.net/wesigj/article/details/141968906"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top