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

设计模式:C++ 模板方法模式 {Template Method in C++}

C++ 模板方法
https://refactoringguru.cn/design-patterns/template-method/cpp/example
https://refactoring.guru/design-patterns/template-method

Template Method in C++
https://refactoring.guru/design-patterns/template-method/cpp/example
https://refactoring.guru/design-patterns/singleton

Template Method is a behavioral design pattern that allows you to define a skeleton of an algorithm in a base class and let subclasses override the steps without changing the overall algorithm’s structure.
模版方法是一种行为设计模式,它在基类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

The Template Method pattern is quite common in C++ frameworks. Developers often use it to provide framework users with a simple means of extending standard functionality using inheritance.
模版方法模式在 C++ 框架中很常见。开发者通常使用它来向框架用户提供通过继承实现的、对标准功能进行扩展的简单方式。

Template Method can be recognized if you see a method in base class that calls a bunch of other methods that are either abstract or empty.
模版方法可以通过行为方法来识别,该方法已有一个在基类中定义的 “默认” 行为。

Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.
模板方法模式是一种行为设计模式,它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

1. Solution

The Template Method pattern suggests that you break down an algorithm into a series of steps, turn these steps into methods, and put a series of calls to these methods inside a single template method. The steps may either be abstract, or have some default implementation. To use the algorithm, the client is supposed to provide its own subclass, implement all abstract steps, and override some of the optional ones if needed (but not the template method itself).
模板方法模式建议将算法分解为一系列步骤,然后将这些步骤改写为方法,最后在 “模板方法” 中依次调用这些方法。步骤可以是抽象的,也可以有一些默认的实现。为了能够使用算法,客户端需要自行提供子类并实现所有的抽象步骤。如有必要还需重写一些步骤 (但这一步中不包括模板方法自身)。

As you can see, we’ve got two types of steps:

  • abstract steps must be implemented by every subclass
    抽象步骤必须由各个子类来实现
  • optional steps already have some default implementation, but still can be overridden if needed
    可选步骤已有一些默认实现,但仍可在需要时进行重写

There’s another type of step, called hooks.A hook is an optional step with an empty body. A template method would work even if a hook isn’t overridden.Usually, hooks are placed before and after crucial steps of algorithms, providing subclasses with additional extension points for an algorithm.
还有另一种名为钩子的步骤。钩子是内容为空的可选步骤。即使不重写钩子,模板方法也能工作。钩子通常放置在算法重要步骤的前后,为子类提供额外的算法扩展点。

2. Structure

TheAbstract Classdeclares methods that act as steps of an algorithm, as well as the actual template method which calls these methods in a specific order. The steps may either be declared abstract or have some default implementation.
抽象类 (Abstract Class) 会声明作为算法步骤的方法,以及依次调用它们的实际模板方法。算法步骤可以被声明为抽象类型,也可以提供一些默认实现。

Concrete Classescan override all of the steps, but not the template method itself.
具体类 (Concrete Classes) 可以重写所有步骤,但不能重写模板方法自身。

3. Applicability (模板方法模式适合应用场景)

Use the Template Method pattern when you want to let clients extend only particular steps of an algorithm, but not the whole algorithm or its structure.
当你只希望客户端扩展某个特定算法步骤,而不是整个算法或其结构时,可使用模板方法模式。

The Template Method lets you turn a monolithic algorithm into a series of individual steps which can be easily extended by subclasses while keeping intact the structure defined in a superclass.
模板方法将整个算法转换为一系列独立的步骤,以便子类能对其进行扩展,同时还可让超类中所定义的结构保持完整。

Use the pattern when you have several classes that contain almost identical algorithms with some minor differences. As a result, you might need to modify all classes when the algorithm changes.
当多个类的算法除一些细微不同之外几乎完全一样时,你可使用该模式。 但其后果就是,只要算法发生变化,你就可能需要修改所有的类。

When you turn such an algorithm into a template method, you can also pull up the steps with similar implementations into a superclass, eliminating code duplication. Code that varies between subclasses can remain in subclasses.
在将算法转换为模板方法时,你可将相似的实现步骤提取到超类中以去除重复代码。子类间各不同的代码可继续保留在子类中。

4. Implement

  1. Analyze the target algorithm to see whether you can break it into steps. Consider which steps are common to all subclasses and which ones will always be unique.
    分析目标算法,确定能否将其分解为多个步骤。从所有子类的角度出发,考虑哪些步骤能够通用,哪些步骤各不相同。
  2. Create the abstract base class and declare the template method and a set of abstract methods representing the algorithm’s steps. Outline the algorithm’s structure in the template method by executing corresponding steps. Consider making the template method final to prevent subclasses from overriding it.
    创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。在模板方法中根据算法结构依次调用相应步骤。可用 final 最终修饰模板方法以防止子类对其进行重写。
  3. It’s okay if all the steps end up being abstract. However, some steps might benefit from having a default implementation. Subclasses don’t have to implement those methods.
    虽然可将所有步骤全都设为抽象类型,但默认实现可能会给部分步骤带来好处,因为子类无需实现那些方法。
  4. Think of adding hooks between the crucial steps of the algorithm.
    可考虑在算法的关键步骤之间添加钩子。
  5. For each variation of the algorithm, create a new concrete subclass. It must implement all of the abstract steps, but may also override some of the optional ones.
    为每个算法变体新建一个具体子类,它必须实现所有的抽象步骤,也可以重写部分可选步骤。

5. Pros and Cons (模板方法模式优缺点)

You can let clients override only certain parts of a large algorithm, making them less affected by changes that happen to other parts of the algorithm.
你可仅允许客户端重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。

You can pull the duplicate code into a superclass.
你可将重复代码提取到一个超类中。

You might violate the Liskov Substitution Principle by suppressing a default step implementation via a subclass.
通过子类抑制默认步骤实现可能会导致违反里氏替换原则。

Template methods tend to be harder to maintain the more steps they have.
模板方法中的步骤越多, 其维护工作就可能会越困难。

6. Relations with Other Patterns

Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method.
工厂方法模式是模板方法模式的一种特殊形式。同时,工厂方法可以作为一个大型模板方法中的一个步骤。

Template Method is based on inheritance: it lets you alter parts of an algorithm by extending those parts in subclasses. Strategy is based on composition: you can alter parts of the object’s behavior by supplying it with different strategies that correspond to that behavior. Template Method works at the class level, so it’s static. Strategy works on the object level, letting you switch behaviors at runtime.
模板方法基于继承机制:它允许你通过扩展子类中的部分内容来改变部分算法。策略模式基于组合机制:你可以通过对相应行为提供不同的策略来改变对象的部分行为。模板方法在类层次上运作,因此它是静态的。策略在对象层次上运作,因此允许在运行时切换行为。

7. The Template Method design pattern

#include <iostream> #include <string> /* * The Abstract Class defines a template method that contains a skeleton of some * algorithm, composed of calls to (usually) abstract primitive operations. * * Concrete subclasses should implement these operations, but leave the template * method itself intact. */ class AbstractClass { public: /* * Virtual destructor is crucial for polymorphic base classes to ensure * derived objects are deleted correctly, preventing memory leaks. */ virtual ~AbstractClass() = default; /* * The template method defines the skeleton of an algorithm. * Marked as 'final' to prevent subclasses from overriding the algorithm's structure. */ void template_method() const { this->base_operation1(); this->required_operation1(); this->base_operation2(); this->hook1(); this->required_operation2(); this->base_operation3(); this->hook2(); } protected: /* * These operations already have default implementations. */ void base_operation1() const { std::cout << "AbstractClass: base_operation1()\n"; } void base_operation2() const { std::cout << "AbstractClass: base_operation2()\n"; } void base_operation3() const { std::cout << "AbstractClass: base_operation3()\n"; } /* * These operations have to be implemented in subclasses. */ virtual void required_operation1() const = 0; virtual void required_operation2() const = 0; /* * These are "hooks." Subclasses may override them, but it's not mandatory * since the hooks already have default (but empty) implementation. Hooks * provide additional extension points in some crucial places of the algorithm. */ virtual void hook1() const {} virtual void hook2() const {} }; /* * Concrete classes have to implement all abstract operations of the base class. * They can also override some operations with a default implementation. */ class ConcreteClass1 final : public AbstractClass { protected: void required_operation1() const override { std::cout << "ConcreteClass1: required_operation1()\n"; } void required_operation2() const override { std::cout << "ConcreteClass1: required_operation2()\n"; } }; /* * Usually, concrete classes override only a fraction of base class' operations. */ class ConcreteClass2 final : public AbstractClass { protected: void required_operation1() const override { std::cout << "ConcreteClass2: required_operation1()\n"; } void required_operation2() const override { std::cout << "ConcreteClass2: required_operation2()\n"; } /* * Overriding a hook to inject custom logic into the algorithm. */ void hook1() const override { std::cout << "ConcreteClass2: hook1()\n"; } }; /* * The client code calls the template method to execute the algorithm. Client * code does not have to know the concrete class of an object it works with, as * long as it works with objects through the interface of their base class. */ void ClientCode(AbstractClass *class_) { // ... class_->template_method(); // ... } int main() { std::cout << "Same client code can work with different subclasses:\n"; ConcreteClass1 *concrete_class1 = new ConcreteClass1; ClientCode(static_cast<AbstractClass*>(concrete_class1)); std::cout << "\n"; std::cout << "Same client code can work with different subclasses:\n"; ConcreteClass2 *concrete_class2 = new ConcreteClass2; ClientCode(static_cast<AbstractClass*>(concrete_class2)); delete concrete_class1; delete concrete_class2; return 0; } 

向上转换 (Upcasting):将派生类的指针或引用安全地转换为基类的指针或引用。这种转换是安全的,因为派生类总是包含基类的部分。

向下转型 (Downcasting):如果你想把基类转回子类,这是不安全的,必须使用 dynamic_cast() 进行显式转换。

在 C++ 中,派生类指针到基类指针的转换是隐式的且安全的,手动显式转换会增加代码冗余。

由于 ConcreteClass1 继承自 AbstractClass,编译器在底层认为 ConcreteClass1 的实例就是一个 AbstractClass 的实例。它包含了基类定义的所有成员。

使用 \n 代替 std::endl 可以获得更好的性能,减少不必要的缓冲区刷新。

Same client code can work with different subclasses: AbstractClass: base_operation1() ConcreteClass1: required_operation1() AbstractClass: base_operation2() ConcreteClass1: required_operation2() AbstractClass: base_operation3() Same client code can work with different subclasses: AbstractClass: base_operation1() ConcreteClass2: required_operation1() AbstractClass: base_operation2() ConcreteClass2: hook1() ConcreteClass2: required_operation2() AbstractClass: base_operation3() 请按任意键继续. . . 
#include <iostream> #include <memory> #include <string> /* * The Abstract Class defines a template method that contains a skeleton of some * algorithm, composed of calls to (usually) abstract primitive operations. * * Concrete subclasses should implement these operations, but leave the template * method itself intact. */ class AbstractClass { public: /* * Virtual destructor is crucial for polymorphic base classes to ensure * derived objects are deleted correctly, preventing memory leaks. */ virtual ~AbstractClass() = default; /* * The template method defines the skeleton of an algorithm. * Marked as 'final' to prevent subclasses from overriding the algorithm's structure. */ void template_method() const { this->base_operation1(); this->required_operation1(); this->base_operation2(); this->hook1(); this->required_operation2(); this->base_operation3(); this->hook2(); } protected: /* * These operations already have default implementations. */ void base_operation1() const { std::cout << "AbstractClass: base_operation1()\n"; } void base_operation2() const { std::cout << "AbstractClass: base_operation2()\n"; } void base_operation3() const { std::cout << "AbstractClass: base_operation3()\n"; } /* * These operations have to be implemented in subclasses. */ virtual void required_operation1() const = 0; virtual void required_operation2() const = 0; /* * These are "hooks." Subclasses may override them, but it's not mandatory * since the hooks already have default (but empty) implementation. Hooks * provide additional extension points in some crucial places of the algorithm. */ virtual void hook1() const {} virtual void hook2() const {} }; /* * Concrete classes have to implement all abstract operations of the base class. * They can also override some operations with a default implementation. */ class ConcreteClass1 final : public AbstractClass { protected: void required_operation1() const override { std::cout << "ConcreteClass1: required_operation1()\n"; } void required_operation2() const override { std::cout << "ConcreteClass1: required_operation2()\n"; } }; /* * Usually, concrete classes override only a fraction of base class' operations. */ class ConcreteClass2 final : public AbstractClass { protected: void required_operation1() const override { std::cout << "ConcreteClass2: required_operation1()\n"; } void required_operation2() const override { std::cout << "ConcreteClass2: required_operation2()\n"; } /* * Overriding a hook to inject custom logic into the algorithm. */ void hook1() const override { std::cout << "ConcreteClass2: hook1()\n"; } }; /* * The client code calls the template method to execute the algorithm. Client * code does not have to know the concrete class of an object it works with, as * long as it works with objects through the interface of their base class. */ void ClientCode(AbstractClass &abstract_instance) { abstract_instance.template_method(); } int main() { std::cout << "Same client code can work with different subclasses:\n"; std::unique_ptr<ConcreteClass1> concrete_class1 = std::make_unique<ConcreteClass1>(); ClientCode(*concrete_class1); std::cout << "\n"; std::cout << "Same client code can work with different subclasses:\n"; std::unique_ptr<ConcreteClass2> concrete_class2 = std::make_unique<ConcreteClass2>(); ClientCode(*concrete_class2); return 0; } 
Same client code can work with different subclasses: AbstractClass: base_operation1() ConcreteClass1: required_operation1() AbstractClass: base_operation2() ConcreteClass1: required_operation2() AbstractClass: base_operation3() Same client code can work with different subclasses: AbstractClass: base_operation1() ConcreteClass2: required_operation1() AbstractClass: base_operation2() ConcreteClass2: hook1() ConcreteClass2: required_operation2() AbstractClass: base_operation3() 请按任意键继续. . . 

References

[1] Yongqiang Cheng (程永强), https://yongqiang.blog.ZEEKLOG.net/
[2] Refactoring.Guru, https://refactoringguru.cn/
[3] Refactoring.Guru, https://refactoring.guru/

Read more

C++ 设计模式概述及常用模式

C++ 设计模式概述 本文介绍了C++中23种设计模式的分类及实现示例,主要分为三大类: 创建型模式(5个):单例模式(常用)、工厂方法模式(常用)、抽象工厂模式(常用)、建造者模式和原型模式。这些模式专注于对象的创建机制。 结构型模式(7个):适配器模式(常用)、桥接模式、组合模式和装饰器模式(常用)等。这些模式处理类和对象的组合方式。 行为型模式:未完整列出,但包含观察者模式等(未展示完整代码)。 文章通过简洁的C++代码示例展示了常用设计模式的实现方法,如单例模式通过私有构造函数和静态方法确保唯一实例,工厂方法模式通过抽象工厂类创建产品等。这些模式为解决特定设计问题提供了可重用的解决方案。 C++ 设计模式概述及常用模式 设计模式可分为三大类:创建型、结构型、行为型。以下是23个设计模式的分类及代码示例: 一、创建型模式(5个) 1. 单例模式(Singleton)⭐ 常用 classSingleton{private:static

By Ne0inhk
C++测试与调试:确保代码质量与稳定性

C++测试与调试:确保代码质量与稳定性

C++测试与调试:确保代码质量与稳定性 一、学习目标与重点 本章将深入探讨C++测试与调试的核心知识,帮助你确保代码的质量与稳定性。通过学习,你将能够: 1. 理解测试与调试的基本概念,掌握测试方法和工具 2. 学会使用单元测试框架,如Google Test和Catch2 3. 理解集成测试的重要性,确保系统的功能正确性 4. 学会使用调试工具,如GDB和Visual Studio调试器 5. 培养测试与调试思维,设计高质量的代码 二、测试的基本概念 2.1 测试的分类 测试可以分为以下几类: * 单元测试:测试单个函数或类的功能 * 集成测试:测试多个模块的集成功能 * 系统测试:测试整个系统的功能 * 验收测试:测试系统是否满足用户需求 * 性能测试:测试系统的性能指标 2.2 测试原则 测试应该遵循以下原则: * 测试应该尽可能早地进行 * 测试应该覆盖所有可能的场景 * 测试应该是自动化的

By Ne0inhk

Java + Vue 毕业设计选题效率提升指南:从脚手架到自动化部署的全链路优化

毕业设计季又到了,对于计算机专业的同学来说,用 Java 做后端,Vue 做前端,是一个非常经典且实用的技术栈组合。但很多同学在真正动手时,常常被各种“琐事”绊住,比如环境配半天、前后端接口对不上、部署时手忙脚乱,导致宝贵的开发时间被大量浪费。今天,我就结合自己带学弟学妹做毕设的经验,聊聊如何通过一套标准化的流程和工具,把 Java + Vue 毕设的开发效率提上去,让你把精力真正花在业务逻辑和创新点上。 1. 毕业设计效率痛点:我们到底在哪儿“卡”住了? 在开始技术选型之前,我们先得搞清楚,做 Java + Vue 毕设时,哪些环节最容易“掉链子”。根据我的观察,主要有这么几个: 1. 环境配置地狱:这是第一个拦路虎。A 同学的 MySQL 是 8.0,B 同学是

By Ne0inhk

Java 接口:从‘空架子’到‘万能遥控器’

🚀Java接口通关秘籍:从“空架子”到“万能遥控器”的逆袭! 发布时间:2026-01-09 专栏:Java基础通关指南 😮 先唠个嗑:为啥接口总被新手“嫌弃”? 刚学Java的小伙伴大概率都有这感受:“接口这玩意儿啥也干不了,就一堆空方法,写了半天还得靠实现类干活,纯纯的‘空架子’?” NONONO!今天咱就把Java接口的底裤扒干净——它不是“空架子”,而是编程界的“万能遥控器”:定义好了按钮(方法),不管是电视、空调还是投影仪(实现类),只要按规矩接这个遥控器,就能按统一的方式干活! 📚 一、啥是Java接口?(人话版解释) 1. 官方定义(快速略过) 接口(Interface)是Java中的一种引用类型,是方法的集合,只能包含常量、抽象方法(Java 8前),以及默认方法、静态方法、私有方法(Java

By Ne0inhk