设计模式: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

JDK21安装与配置教程

JDK21安装与配置教程

文章目录 * 一、下载JDK * 1. 下载地址 * 2. 下载JDK21 * 二、JDK21安装及配置 * 1. 解压zip压缩包 * 2. 配置Java环境变量 * 2.1 打开系统属性设置 * 2.2 新建系统环境变量 * 2.3 编辑 PATH 环境变量 * 2.4 验证环境变量是否配置成功 一、下载JDK 1. 下载地址 华为云镜像下载地址: 地址 1(OracleJDK):https://repo.huaweicloud.com/java/jdk/ 地址 2(OpenJDK):https://mirrors.huaweicloud.com/openjdk/ 地址

By Ne0inhk

开发者环境配置最佳实践:用 mise 一个工具管好 Java、Node 和 Python等开发环境

前面咱们把黑色的新终端、D 盘的 Linux 系统还有 Cursor 编辑器都搞定了。但这台电脑现在还只是个空壳,咱们写代码需要的 Java、Node.js、Python 这些环境还没装。 以前大家习惯去官网下个 JDK 的 exe 安装包,或者用 nvm 换 Node 版本,这种做法在 2026 年真的有点落伍了,而且环境多了特别容易乱。今天咱们聊聊目前独立开发者圈子里的全神器:mise。 为什么不再用传统的安装方式 大家肯定遇到过这种情况:为了学 Java 装了个 JDK 8,后来做新项目又要 JDK 17 或 21,电脑里攒了一堆安装包,环境变量配来配去最后连自己都晕了。 咱们现在的最佳实践是在 WSL里用 mise。它这一个工具就能管好几乎所有的编程语言。它最大的好处就是“绿色”

By Ne0inhk
2025年第十六届蓝桥杯省赛JavaB组真题回顾

2025年第十六届蓝桥杯省赛JavaB组真题回顾

第16届蓝桥杯省赛已经结束了,第一次参加也是坐牢了4个小时,现在还是来总结一下吧(先声明以下的解法,大家可以当作一种思路来看,解法不一定是正解,只是给大家提供一种能够正常想到的思路吧) 试题A:逃离高塔 本题其实没有什么难度,就是一个循环遍历即可,那么唯一需要注意的就是循环遍历的过程中,int是会爆的,这里需要用long来进行存储 public class Main{ public static void main(String[] args){ int ans=0;//记录最终答案 for(long i=1;i<=2025;i++){ long x=i*i*i; if(n%10==3){ ans++; } } System.out.println(ans); } } ​  最后进行的答案就是:

By Ne0inhk
JavaScript DOM 核心操作:从内容到节点的实战指南

JavaScript DOM 核心操作:从内容到节点的实战指南

DOM(文档对象模型)是前端开发中操作页面结构、内容和样式的核心,本文聚焦 DOM 中元素内容、属性、样式的读写修改,以及节点的增删改,结合实战示例讲解核心用法与最佳实践。 一、操作元素内容 元素内容操作分为纯文本处理和带 HTML 结构的处理,核心使用 innerText 和 innerHTML 两个属性,二者特性对比如下: 方法识别 HTML 标签保留换行 / 空格标准性适用场景innerText❌❌非标准(IE)仅读取 / 修改纯文本innerHTML✅✅W3C 标准读取 / 修改带 HTML 结构的内容 1. innerText:纯文本操作 仅处理文本内容,会忽略 HTML 标签和源码中的换行 / 空格,适合简单文本读写。 // 读操作:获取元素纯文本内容 var text = element.innerText;

By Ne0inhk