设计模式 19 模板模式 Template Pattern

设计模式 19 模板模式 Template Pattern

1.定义

        模板模式(Template Pattern)是一种行为设计模式,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。在模板模式中,定义了一个抽象类,其中包含了一个模板方法(template method),这个方法定义了算法的基本结构和步骤,但其中的具体步骤由子类来实现。

        模板模式主要用于在不同子类中封装通用的行为,同时保持整体算法结构的一致性。通过模板方法模式,使得父类能够控制方法的执行顺序,同时细节延迟到子类实现。

2.内涵

模板模式包含以下几个角色:

  1. Abstract Class(抽象类):定义了一个模板方法,该方法是算法的骨架,其中调用了一系列抽象或具体的步骤。抽象类可能还包含了一些通用的实现,这些实现可以在模板方法中直接调用,也可以在需要时在子类中覆盖。
  2. Concrete Class(具体类):继承自抽象类,并实现了其中的具体步骤。每个具体类可以根据需要实现抽象类中定义的方法,并将其组合在一起形成完整的算法。

通过模板模式,可以实现代码复用、减少重复代码的编写,同时保持算法的统一性和一致性。模板模式能够提供一个稳定的算法框架,同时允许子类自由扩展或修改算法的某些部分。

=======================
|      AbstractClass   |
|----------------------|
|+ templateMethod()   |  
|+ primitiveOperation1()|
|+ primitiveOperation2()|
=======================
             |
             |
             |
             |
      =====================
      |    ConcreteClassA   |
      |----------------------|
      |# primitiveOperation1()|
      |# primitiveOperation2()|
      =====================
             |
             |
             |
             |
     =====================
     |   ConcreteClassB    |
     |----------------------|
     |# primitiveOperation1()|
     |# primitiveOperation2()|
     =====================


     
AbstractClass(抽象类):这是模板模式的核心部分,抽象类定义了模板方法 templateMethod() 和两个抽象方法 primitiveOperation1() 和 primitiveOperation2(),其中 templateMethod() 指定了算法的框架,包括一系列调用步骤,而 primitiveOperation1() 和 primitiveOperation2() 则是需要在具体子类中实现的具体步骤。

ConcreteClassA 和 ConcreteClassB(具体类):这是实际实现模板模式的具体子类。它们继承了 AbstractClass 并实现了其中的抽象方法 primitiveOperation1() 和 primitiveOperation2()。每个具体类可能实现完全不同的具体步骤,但是它们遵循相同的模板方法结构(templateMethod())。

     
     
3.使用示例

/**
 * The Abstract Class defines a template method 
 */
class AbstractClass {
  /**
   * The template method defines the skeleton of an algorithm.
   */
 public:
  void TemplateMethod() const {
    this->BaseOperation1();
    this->RequiredOperations1();
    this->BaseOperation2();
    this->Hook1();
    this->RequiredOperation2();
    this->BaseOperation3();
    this->Hook2();
  }

 protected:
  void BaseOperation1() const {
    std::cout << "AbstractClass says: I am doing the bulk of the work\n";
  }
  void BaseOperation2() const {
    std::cout << "AbstractClass says: But I let subclasses override some operations\n";
  }
  void BaseOperation3() const {
    std::cout << "AbstractClass says: But I am doing the bulk of the work anyway\n";
  }
  
  
  virtual void RequiredOperations1() const = 0;
  virtual void RequiredOperation2() const = 0;
  
  
  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 : public AbstractClass {
 protected:
  void RequiredOperations1() const override {
    std::cout << "ConcreteClass1 says: Implemented Operation1\n";
  }
  void RequiredOperation2() const override {
    std::cout << "ConcreteClass1 says: Implemented Operation2\n";
  }
};


class ConcreteClass2 : public AbstractClass {
 protected:
  void RequiredOperations1() const override {
    std::cout << "ConcreteClass2 says: Implemented Operation1\n";
  }
  void RequiredOperation2() const override {
    std::cout << "ConcreteClass2 says: Implemented Operation2\n";
  }
  void Hook1() const override {
    std::cout << "ConcreteClass2 says: Overridden Hook1\n";
  }
};

void ClientCode(AbstractClass *class_) {
  // ...
  class_->TemplateMethod();
  // ...
}

int main() {
  std::cout << "Same client code can work with different subclasses:\n";
  ConcreteClass1 *concreteClass1 = new ConcreteClass1;
  ClientCode(concreteClass1);
  std::cout << "\n";
  std::cout << "Same client code can work with different subclasses:\n";
  ConcreteClass2 *concreteClass2 = new ConcreteClass2;
  ClientCode(concreteClass2);
  delete concreteClass1;
  delete concreteClass2;
  return 0;
}


输出:

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway

上述代码类图


4.注意事项

在使用模板模式(Template Pattern)时,有一些注意事项需要注意以避免可能的问题和踩坑:

  • 抽象类的设计:在定义抽象类时,需要仔细考虑模板方法中的逻辑和步骤顺序,确保所有子类都能正确实现这些步骤。避免在抽象类中定义过多的具体实现,应保持抽象类的简洁性,只包含框架结构和必要的抽象方法。
  • 子类实现的一致性:子类在实现抽象方法时,需要保持接口一致性,确保方法签名和返回类型与父类中定义的一致。否则可能会导致编译错误或运行时异常。
  • 算法的扩展性:模板模式允许子类修改或扩展算法的某些部分,但注意避免过度扩展和修改,以免破坏算法的一致性和规范性。确保扩展的功能是相对独立且有必要的。
  • 模板方法的执行顺序:模板方法定义了算法的执行顺序,子类在实现具体步骤时要遵循这个顺序。任何不当的调整可能会导致算法步骤的混乱或错误。
  • 继承的关系:模板模式使用了继承机制,但应该避免过度使用继承导致类层次结构复杂。确保继承关系的合适性和合理性。
  • 钩子方法:在抽象类中可以定义钩子方法,用于控制算法的某些部分是否执行。需要谨慎设计和使用钩子方法,避免影响算法的整体结构和正确性。
  • 单一职责原则:确保每个具体类只负责实现自己的具体步骤,遵循单一职责原则,避免一个类承担过多的责任和功能。
5.最佳实践


在开发中实现模板模式(Template Pattern)时,可以采用以下一些比较好的经验:

  • 合理抽象和分离关注点:在设计抽象类时,需要将通用的算法步骤抽象出来,同时将具体步骤留给具体子类实现。这样可以实现算法的复用和解耦,同时方便后续的扩展和修改。
  • 使用钩子方法:钩子方法是一种可以控制算法流程的手段,可以在抽象类中定义一些空方法或默认实现,具体子类可以选择性地重写这些方法来影响算法的执行。这样可以在不改变模板方法结构的情况下灵活地扩展和定制算法。
  • 保持一致性:确保所有具体子类实现的具体步骤都是与抽象类中定义的一致的,保持算法的一致性和规范性,避免出现错误或混乱。
  • 封装变化部分:在设计模板模式时,将会变化的部分抽象出来,以便随时扩展和修改,而将不变的部分固定在模板方法中,确保算法的稳定性和可维护性。
  • 使用工厂方法:在具体类的实例化时,可以考虑使用工厂方法模式(Factory Method Pattern),将实例化过程放在具体的工厂类中,以便灵活地切换不同的具体子类。

6.总结

通过模板模式,可以实现代码复用、减少重复代码的编写,同时保持算法的统一性和一致性。模板模式能够提供一个稳定的算法框架,同时允许子类自由扩展或修改算法的某些部分。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/651703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[论文笔记]Chain-of-Thought Prompting Elicits Reasoning in Large Language Models

引言 今天带来思维链论文 Chain-of-Thought Prompting Elicits Reasoning in Large Language Models的笔记。 作者探索了如何通过生成一系列中间推理步骤的思维链&#xff0c;显著提升大型语言模型在进行复杂推理时的能力。 1 总体介绍 语言模型的规模扩大已被证明能够带来…

基于Netty实现安全认证的WebSocket(wss)服务端

1.Netty服务端 服务端代码参考【基于Netty实现WebSocket服务端-CSDN博客】中的两种方式都可以&#xff1b;这里用的是第一种简单方式。 新增如下逻辑&#xff1a;添加SSLHandler SSLContext sslContext SslUtil.createSSLContext("JKS","D:\\workSpace\\day…

编译qt5.15.2(mac/windows)的mysql驱动(附带编译好的文件)

文章目录 0 背景1 编译过程2 福利 0 背景 因为需要连接到mysql数据库&#xff0c;所以需要连mysql驱动。 1 编译过程 1&#xff0c;打开文件/Users/mac/Qt5.14.2/5.14.2/Src/qtbase/src/plugins/sqldrivers/sqldrivers.pro&#xff0c;注释掉QMAKE_USE mysql&#xff1b; 如…

[数智人文实战] 02.舆情分析之词云可视化、文本聚类和LDA主题模型文本挖掘

【数智人文与文本挖掘】知识星球建立且正式运营,欢迎新老博友和朋友加入,一起分享更多数智人文知识和交流进步。该星球计划每周至少分享7个资源或文章,包括数智人文、文本挖掘、人工智能、大数据分析和图书情报的技术文章、代码及资源。同时,欢迎进入星球的朋友咨询我图情和…

多线程基本常识

多线程的状态 在Java中&#xff0c;一个线程的生命周期有以下几种状态&#xff1a; 新建&#xff08;New&#xff09;&#xff1a;当线程对象被创建时&#xff0c;线程处于新建状态。此时线程对象存在&#xff0c;但还没有调用start()方法启动线程。 运行&#xff08;Runnable…

Vulnhub - AI-WEB-1.0靶机教程

目录 站点信息收集 c段扫描 端口扫描 目录扫描 漏洞利用 使用 burp 抓包 查询数据库名 查询数据库下的表 查询表中的字段名 查询字段中的数据 --os-shell 上传一句话木马 下载地址&#xff1a;https://download.vulnhub.com/aiweb/AI-Web-1.0.7z 我们从站点信息收…

【C/C++】观察者模式

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

LAMP网络服务架构

目录 LAMP 网站服务架构 LAMP的组成部分 LAMP的构建顺序 安装论坛 0.电脑已编译安装Apache&#xff0c;MySQL&#xff0c;PHP 1.创建数据库&#xff0c;并进行授权 2.上传论坛压缩包到 /opt ,并解压 3.上传站点更新包 4.更改论坛目录的属主 5.浏览器访问验证 LAMP 网…

授权调用: 介绍 Transformers 智能体 2.0

简要概述 我们推出了 Transformers 智能体 2.0&#xff01; ⇒ &#x1f381; 在现有智能体类型的基础上&#xff0c;我们新增了两种能够 根据历史观察解决复杂任务的智能体。 ⇒ &#x1f4a1; 我们致力于让代码 清晰、模块化&#xff0c;并确保最终提示和工具等通用属性透明化…

PPT大珩助手新功能-生成迷宫

大珩助手是一款功能丰富的办公软件插件&#xff0c;它主要分为两个版本&#xff1a;PPT大珩助手和Word大珩助手。这两个版本都旨在提高用户在处理演示文稿和文档时的效率。 PPT大珩助手 这是一款专门为Microsoft PowerPoint设计的插件。它提供了多种功能&#xff0c;例如素材…

深入理解Kubernetes的调度核心思想

一、引言 Kubernetes&#xff08;简称K8s&#xff09;是一个开源的容器编排系统&#xff0c;用于自动化部署、扩展和管理容器化应用程序。在Kubernetes集群中&#xff0c;调度器是一个核心组件&#xff0c;它负责将Pod&#xff08;Kubernetes中的最小部署单元&#xff09;分配…

windows内存管理

一 windows系统的内存管理涉及哪些 1.1 虚拟内存管理机制 windows操作系统使用虚拟内存技术&#xff0c;将磁盘文件&#xff0c;通过映射对象&#xff08;存储在物理内存&#xff09;关联&#xff0c;映射到虚拟内存作为文件试图。即用户操作"虚拟内存中File View Objec…

【C语言】10.C语言指针(2)

文章目录 1.数组名的理解2.使用指针访问数组3.一维数组传参的本质4.冒泡排序算法步骤 5.二级指针6.指针数组7.指针数组模拟二维数组 1.数组名的理解 int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0];这里我们使用 &arr[0] 的方式拿到了数组第一个元素的地址&am…

Stable Diffusion【写实模型】:逼真,逼真,超级逼真的国产超写实摄影大模型万享XL

今天和大家分享的是一个国产万享系列中使用量最高的大模型:万享XL_超写实摄影&#xff0c;顾名思义&#xff0c;该大模型主要是面向写实摄影&#xff0c;一方面生成的图片人物皮肤纹理细节超级逼真&#xff0c;另一方面对于光影效果的处理也非常到位。对于万享XL超写实摄影大模…

数组单调栈-901. 股票价格跨度、leetcode

单调栈作为一种数据结构在求解类递增、递减方面的题目中有较为广泛的应用&#xff0c;在以往的leetcode中所见到的相关单调栈的题目均为单一元素&#xff0c;今天刷到901题目时&#xff0c;想到了将数组元素作为单调栈中元素的方法进行求解。 题目链接及描述 901. 股票价格跨…

秋招突击——算法打卡——5/27——复习{寻找特定中位数}——新做:{最长回文字串、Z 字形变换}

文章目录 复习——寻找特定中位数新作——最长回文子串个人思路分析实现代码参考学习和上述思路相同&#xff0c;枚举中心点字符串哈希二分 新作——Z 字形变换个人做法思路分析实现代码 参考解法分析总结 复习——寻找特定中位数 第一次的链接&#xff1a;寻找中位数本来以为…

Reactor模式Proactor模式

1.Reactor/Dispatcher模式 1.1 概述 Reactor模式下&#xff0c;服务端的构成为Reactor 处理资源池。其中&#xff0c;Reactor负责监听和分发事件&#xff0c;而处理资源池则负责处理事件。 该模式下的组合方案有下面几种(第三种几乎没有被实际应用)&#xff1a; 1 * Reacto…

CRLF注入漏洞

1.CRLF注入漏洞原理 Nginx会将 $uri进行解码&#xff0c;导致传入%0a%0d即可引入换行符&#xff0c;造成CRLF注入漏洞。 执行xss语句 2.漏洞扩展 CRLF 指的是回车符(CR&#xff0c;ASCII 13&#xff0c;\r&#xff0c;%0d) 和换行符(LF&#xff0c;ASCII 10&#xff0c;\n&am…

栈 队列

目录 1.1栈的基本概念 1.1.1栈的定义 1.1.2栈的基本操作 1.2栈的顺序存储结构 1.2.1构造原理 1.2.2基本算法 1.3栈的链式存储结构 1.3.1构造原理 1.3.2基本算法 2.1队列的基本概念 2.1.1队列的定义 2.1.2队列的基本运算 2.2队列的顺序存储结构 2.2.1构造原理 2.2.1基…

C++ | Leetcode C++题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:int numDistinct(string s, string t) {int m s.length(), n t.length();if (m < n) {return 0;}vector<vector<unsigned long long>> dp(m 1, vector<unsigned long long>(n 1));for (i…