C++设计模式_03_模板方法Template Method

文章目录

  • 1. 设计模式分类
    • 1.1 GOF-23 模式分类
    • 1.2 从封装变化角度对模式分类
  • 2. 重构(使用模式的方法)
    • 2.1 重构获得模式 Refactoring to Patterns
    • 2.2 重构关键技法
  • 3. “组件协作”模式
  • 4. Template Method 模式
    • 4.1 动机( Motivation)/应用场景
      • 4.1.1 结构化软件设计流程
      • 4.1.2 面向对象软件设计流程
      • 4.1.3 对比两种写法:
    • 4.2 早绑定和晚绑定
    • 4.3 模式定义
    • 4.4 结构( Structure)
    • 4.5 要点总结

上篇介绍了面向对象设计的原则和目标之后,本篇将会介绍非常经典,并且具有示范效应的模式-模板方法Template Method。Template Method模式是一种 非常基础性的设计模式,在面向对象系统中有着大量的应用。它用 最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的 扩展点(继承+多态),是代码复用方面的基本实现结构。( 只要写过面向对象的应用程序,一定用过Template Method,只是可能没有写过核心流程

1. 设计模式分类

1.1 GOF-23 模式分类

首先看一下,在《设计模式:可复用面向对象软件的基础》中对23种设计模式整体有如下分类方法:

  • 从目的来看:
    • 创建型( Creational) 模式: 将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。
    • 结构型( Structural) 模式: 通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。
    • 行为型( Behavioral) 模式 : 通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。
  • 从范围(实现手段)来看
    • 类模式处理类与子类的静态关系:更偏重于继承方案
    • 对象模式处理对象间的动态关系:更偏重于组合方案

1.2 从封装变化角度对模式分类

在实践中总结的一种分类方式如下:

  • 组件协作:解决协作问题
    • Template Method
    • Observer / Event
    • Strategy
  • 单一职责:解决类与类之间责任划分的问题
    • Decorator
    • Bridge
  • 对象创建:解决对象创建过程中的依赖关系
    • Factory Method
    • Abstract Factory
    • Prototype
    • Builder
  • 对象性能:
    • Singleton
    • Flyweight
  • 接口隔离:
    • Façade
    • Proxy
    • Mediator
    • Adapter
  • 状态变化:
    • Memento
    • State
  • 数据结构:
    • Composite
    • Iterator
    • Chain of Resposibility
  • 行为变化:
    • Command
    • Visitor
  • 领域问题:
    • Interpreter

2. 重构(使用模式的方法)

2.1 重构获得模式 Refactoring to Patterns

学习设计模式中非常重要的方法

  • 面向对象设计模式是“好的面向对象设计”,所谓“好的面向对象设计”指是那些可以满足 “应对变化,提高复用”的设计 。

  • 现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点,然后在变化点处应用设计模式,从而来更好地应对需求的变化” 。“什么时候、什么地点应用设计模式”比“理解设计模式结构本身”更为重要。

  • 设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发实践提倡的“Refactoring to Patterns” 是目前普遍公认的最好的使用设计模式的方法。
    对本条的理解:没有使用模式的情况下,代码的结构关系是怎样的,存在什么样的问题,违背了怎样的设计原则,通过迭代重构的方式去修正他,通过修正得到了一种良好的解决方案,得到一种模式。推荐在工作中也是采用这种方式来得到一种模式,除非你已经在该领域有丰富的经验,从而对模式的使用很有把握。

  • 推荐图书
    在这里插入图片描述
    这两本书代码和思想是与后期博文介绍的思想都是很类似的。

2.2 重构关键技法

以下的五种重构技巧,目前的理解可能还不够到位,但是原则很重要,技法也是很重要。

  • 静态 -> 动态

  • 早绑定 -> 晚绑定

  • 继承 -> 组合

  • 编译时依赖 -> 运行时依赖

  • 紧耦合 -> 松耦合
    其实上面五种技巧讲的是一件事情,也可以看做是从不同角度看待同一个问题。

3. “组件协作”模式

  • 现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。

  • 典型模式

    • Template Method 模板方法
    • Strategy 策略模式
    • Observer / Event 事件模式
      当我们介绍这三种模式是“组件协作”模式时,并不是说其他模式与“组件协作”模式没有关系,其实也有关系,只是上述三种模式体现的最为强烈,特征表现特别明显。

4. Template Method 模式

4.1 动机( Motivation)/应用场景

  • 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。

  • 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?

4.1.1 结构化软件设计流程

有以下代码,程序库开发人员开发的class Library,其中包含了完成某个任务的几个步骤,假设为step1,step3,step5。

(1)template1_lib.cpp:

//程序库开发人员
class Library{public:void Step1(){//...}void Step3(){//...}void Step5(){//...}
};

作为应用程序开发人员,为了实现功能,class Application中也做了step2,step4这2个步骤,并且开发了main函数。

完成步骤的建立之后,在main函数中,以某种具体流程串起来。以下代码中线执行流程为:先执行lib.Step1,再根据app.Step2的返回值来执行lib.Step3,再重复执行app.Step4四次,最后执行lib.Step5

(2)template1_app.cpp:

//应用程序开发人员
class Application{
public:bool Step2(){//...}void Step4(){//...}
};int main()
{Library lib();Application app();lib.Step1();if (app.Step2()){lib.Step3();}for (int i = 0; i < 4; i++){app.Step4();}lib.Step5();}

博文中展示的代码是没有遵循C++的编码规范的。

4.1.2 面向对象软件设计流程

还有第二种做法
程序库开发人员开发的class Library,除了包含step1,step3,step5,同时也将step2,step4写下,但是不做实现。
大家在以前的开发中经常会碰到代码样例,早期做windows程序开发时,微软会推荐先做windows典型应用程序的流程,会提供类似的样例代码,说明需要做什么步骤,哪些可以直接调用,就像step1,step3,step5,哪些需要自己去写,例如step2,step4。
其实框架人员已经开发好了整体流程,常常是不需要更改,也就是稳定的,所以框架开发人员或者说程序库开发人员,完全可以将流程写下来,这里的流程是和上面代码表达的流程是一样的,只不过是由框架开发人员去写。

(1)template2_lib.cpp:

//程序库开发人员
class Library{
public://稳定 template methodvoid Run(){Step1();if (Step2()) { //支持变化 ==> 虚函数的多态调用Step3(); }for (int i = 0; i < 4; i++){Step4(); //支持变化 ==> 虚函数的多态调用}Step5();}virtual ~Library(){ }protected:void Step1() { //稳定//.....}void Step3() {//稳定//.....}void Step5() { //稳定//.....}//框架开发人员无法决定怎么去写,留给子类去重写virtual bool Step2() = 0;//变化virtual void Step4() =0; //变化
};

(2)template2_app.cpp:

子类作为应用程序开发人员,重写Step2,Step4。
Library* pLib=new Application()pLib多态指针,其声明类型为Library,实际类型为Application,当他调用虚函数的时候,就会按照虚函数动态绑定的规则去调用。
lib->Run()是非虚函数,但是其里面Step2,Step4是虚函数,因此其会按照虚函数的调用规则去找子类Application的实现。

//应用程序开发人员
class Application : public Library {
protected:virtual bool Step2(){//... 子类重写实现}virtual void Step4() {//... 子类重写实现}
};int main(){Library* pLib=new Application();lib->Run();delete pLib;}
}

细节:
virtual ~Library(){ }:在C++中写一个基类,有一条原则就是将基类中的析构函数写成虚的,这样就可以在delete pLib调用到子类的析构函数

4.1.3 对比两种写法:

方法一 是一种结构化软件设计流程,其结构图如下:
在这里插入图片描述
方法二 是面向对象软件设计流程,其结构图如下:
在这里插入图片描述
在方法二中将程序主流程写到了Library中,应用程序就相对写的少了。而且可以看到方法一中是蓝色框调用红色框,而方法二中是红色框调用蓝色框。

4.2 早绑定和晚绑定

对上述方法进行梳理可以看到第一种方法是一种早绑定的方法,因为Library天然是写的早的,Application写的晚,利用晚的东西调用早的东西就是早绑定,这是编程语言默认的做法。
但是在面向对象软件语言以后,Library还是写的早的,Application还是写的晚,而使用Library调用Application,这样就是晚绑定。
在这里插入图片描述

4.3 模式定义

GoF中对模板方法模式的定义如下:
定义一个操作中的算法的骨架(对应第二种方法的run函数) (稳定),而将一些步骤延迟(延迟一般代表定义一个虚函数,让子类去实现虚函数,也就是支持子类来变化)(变化)到子类中。 Template Method使得子类可以不改变(复用)一个算法的结构,即可重定义(override 重写)该算法的某些特定步骤。 —《设计模式》GoF

可以参考方法二中的代码进行映射理解的。以下代码深刻揭示了绝大多数设计模式的最核心的结构特点就是稳定中有变化,run()是稳定的,Step2()、Step4()是变化的,在C++语言层面体现出来的就是,稳定的代码需要写成非虚函数,要支持变化的要写成虚函数。

	//稳定 template methodvoid Run(){Step1();if (Step2()) { //支持变化 ==> 虚函数的多态调用Step3(); }for (int i = 0; i < 4; i++){Step4(); //支持变化 ==> 虚函数的多态调用}Step5();}

那么这种模式有什么缺点?
前面假定Run()是稳定的,但是假如Run()不稳定了,也就不适合使用Template Method,因此该设计模式使用的前提就是Run()是稳定的。

当软件体系结构中所有都不稳定的时候,任何一种模式都不可使用,这是因为设计模式假设条件是必须有一个稳定点。

反过来,当Step2()、Step4()都是稳定的时候,设计模式也就没有使用的意义。

设计模式最大的作用就是变化和稳定之间寻找隔离点,然后来分离他们,从而来管理变化,按照compact的讲法(不懂什么一次),就是将变化像小兔子一样关进笼子,让其在笼子里跳,而不至于跳出来将整个房间污染。所以正常的软件结构,一定是既有变化又有稳定点的。

在模式应用的时候,核心就是分辨出来软件体系结构中,哪些是稳定的,哪些是变化的。

有了这个意识之后,再看下图,程序主流程Run()为什么放在红框中,这是因为我们假定他是想对稳定的,更具有复用价值。
在这里插入图片描述
绝大对数软件框架中都有Template Method模式,相对于第一种方法,使用Template Method模式,应用程序的核心流程是塞到父类里,所以应用程序开发人员是看不到变化过程,只需要写几个步骤就可以了。
所以在面向对象的Template Method模式下,如果你是application的开发人员,经常会面对一种“只见树木,不见森林”的困惑,因为你只是在写子步骤,而没有去写核心流程。

4.4 结构( Structure)

在这里插入图片描述
上图是《设计模式》GoF中定义的Template Method的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。
在这里插入图片描述
看任何设计模式的时候,包括其类图,都画一画哪些是稳定的,哪些是变化的,当你形成了这样一种习惯之后,你对模式的理解会更上一层楼,而不是只看其代码关系。

4.5 要点总结

  • Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点(继承+多态),是代码复用方面的基本实现结构。(只要写过面向对象的应用程序,一定用过Template Method,只是可能没有写过核心流程)

  • 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。

    • 在Template Method设计之前,软件体系架构的主流是应用程序开发人员来调用Library开发人员写的代码,当面向对象的设计模式成为主流之后,调用关系反转,让早写的来调用晚写的,而依靠的机制就是虚函数的机制,也就是虚函数的晚绑定机制
    • 虚函数是面向对象里面最核心的晚绑定机制,但是任何一个编程语言的晚绑定机制不只有虚函数,像C++中还有函数指针,但是函数指针在某些场合不具有虚函数的抽象性
  • 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法(前后有流程环境才有意义)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

磐基2.0搭建es集群

参考&#xff1a; k8s安装elasticsearch集群 k8s安装elasticsearch集群_k8s部署elasticsearch集群_MasonYyp的博客-CSDN博客1 环境简述搭建es集群需要使用的技术如下&#xff1a;k8s集群、StatefulSet控制器、Service&#xff08;NodePort&#xff09;服务、PV、PVC、volumeC…

洛谷 LGR SCP-J 2023 c++语言模拟试题 10. 以下程序片段的时间复杂度为( )

之前在牛客的一个群中看到有位哥们发的题 好像是洛谷哪次的模拟题&#xff0c;还写着什么 LGR SCP-J 2023 c语言模拟试题 题目 就是给段代码询问时间复杂度 for (int i1; i<n; i){for (int j1; j<n; ji){for (int k1; k<n; k j){}} } 跑代码 一开始想不出怎么解就…

数据结构(C语言版)概念、数据类型、线性表

数据结构&#xff08;C语言&#xff09;基本概念 数据的基本单位 数据的基本单位是位&#xff08;bit&#xff09;和字节&#xff08;byte&#xff09;。位是最小的存储单位&#xff0c;它可以表示一个二进制的0或1。字节由8个位组成&#xff0c;用于表示一个字符或数字。在计…

ElasticSearch第二讲:ES详解 - ElasticSearch基础概念

ElasticSearch第二讲&#xff1a;ES详解 - ElasticSearch基础概念 在学习ElasticSearch之前&#xff0c;先简单了解下ES流行度&#xff0c;使用背景&#xff0c;以及相关概念等。本文是ElasticSearch第二讲&#xff0c;ElasticSearch的基础概念。 文章目录 ElasticSearch第二讲…

将PyCharm中的终端运行前面的PS修改成当前环境

最近使用Pycharm中的Terminal来pip安装一些pakage&#xff0c;发现Terminal运行前面的显示的是PS&#xff0c;然后输入安装指令报错。“python无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。” 解决方法&#xff1a; 只需要在pycharm的设置中修改一些termi…

01JVM_内存结构

一、什么是JVM 1.JVM的定义 Java程序的运行环境&#xff0c;java二进制字节码的运行环境 2.JVM的好处 ①一次编写&#xff0c;到处运行 ②自动内存管理&#xff0c;垃圾回收功能 ③数组下标越界检查 ④多态 3.jvm&#xff0c;jre&#xff0c;jdk的比较 3.常见的JVM 主…

Redis限流实践:实现用户消息推送每天最多通知2次的功能

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

java从入门到起飞(八)——循环和递归

文章目录 Java循环1. 什么是循环&#xff1f;1.1 为什么需要循环&#xff1f;1.2 循环的分类 2. Java中的循环结构2.1 for循环2.2 while循环2.3 do-while循环 3. 循环控制语句3.1 break语句3.2 continue语句 4. 总结 Java递归1. 什么是递归2. 递归的原理3. 递归的实现4. 递归的…

更换 yum 阿里源 - 手把手教你怎么配置,在也不需要求别人了 - 看懂一个就相当于看懂了其他的linux系统

更换阿里源 我的是centos8 当然 centos7 也可以换 后面有更详细的怎么配 &#xff0c;再也不用求别人怎么弄了 最直接的方式 直接复制 执行 centos7 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo或者 wget -O /etc/yum.repos.…

基于SpringBoot + Vue的项目整合WebSocket的入门教程

1、WebSocket简介 WebSocket是一种网络通信协议&#xff0c;可以在单个TCP连接上进行全双工通信。它于2011年被IETF定为标准RFC 6455&#xff0c;并由RFC7936进行补充规范。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性…

酷雷曼第二期无人机技能培训圆满举办

第2期无人机技能培训 2023年8月24日-8月25日&#xff0c;第二期酷雷曼无人机技能培训及执照考试在北京圆满举办&#xff0c;来自五湖四海、全国各地的合作商千里相聚&#xff0c;培训现场热闹融洽&#xff0c;再续精彩盛况。 随着《无人驾驶航空器飞行管理暂行条例》正式发布…

性能测试 —— Jmeter 命令行详细

我们在启动Jmeter时 会看见&#xff1a;Don’t use GUI mode for load testing !, only for Test creation and Test debugging.For load testing, use CLI Mode (was NON GUI) 这句话的意思就是说&#xff0c;不要使用gui模式进行负载测试&#xff0c;gui模式仅仅是创建脚本…

Mysql JSON

select json_extract(c2, $.a) select c2->"$.a" // json_extract的语法糖 &#xff08;取出的值会保留"双引号" so不适合实战&#xff09; 注&#xff1a;mysql若是引擎Mariadb则不支持json操作符-&#xff1e;&#xff1e;语法糖 select c2->…

扫地僧静态养站王站群:搜狗SEO站群收录养站效果

扫地僧静态养站王站群:Sogou搜狗SEO出站及收录效果,扫地僧静态站群采用了静态生成式的方式&#xff0c;只需要一个后台管理系统即可管理多个网站&#xff0c;大大提高了建站效率。建站大概45天左右&#xff0c;收录率百分之三十至百分之五十左右 如果对购买的域名进行把控&…

游戏思考30(补充版):关于逆水寒铁牢关副本、白石副本和技能的一些注释(2023/0902)

前期介绍 我是一名逆水寒的玩家&#xff0c;做一些游戏的笔记当作攻略记录下来&#xff0c;荣光不朽-帝霸来源视频连接 传送门 一、旧版铁牢关&#xff08;非逆水寒老兵服&#xff09; &#xff08;1&#xff09;老一&#xff1a;巨鹰 1&#xff09;机制一&#xff1a;三阵风…

ubuntu20.04 Supervisor 开机自启动脚本一文配置

前言: 最近发现一种非常好的开机启动服务方式,不光可以开机自启动,而且还可以进行开机节点的进程守护,这样大大确保了线程的稳定情况,这种服务甚至可以守护开机的进程,所以比之前设置 rc.local 开机自启动脚本一文配置节点好出很多,它甚至可以使用网页登录监管我开机自启…

手写RPC框架--4.服务注册

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧) RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧) 服务注册 服务注册a.添加服务节点和主机节点b.抽象注册中心c.本地服务列表 服务注册 a.添加服务节点和主机节点 主要完成服务注册和发现的功能&#xff0c;其具体流程如下&…

【Springcloud】elk分布式日志

【Springcloud】elk分布式日志 【一】基本介绍【二】Elasticsearch【1】简介【2】下载【3】安装【4】启动 【三】Logstash【1】简介【2】下载【3】安装【4】启动 【四】Kibana【1】简介【2】下载【3】安装【4】启动 【五】切换中文【六】日志收集 【一】基本介绍 &#xff08;…

【测试开发】Mq消息重复如何测试?

本篇文章主要讲述重复消费的原因&#xff0c;以及如何去测试这个场景&#xff0c;最后也会告诉大家&#xff0c;目前互联网项目关于如何避免重复消费的解决方案。 Mq为什么会有重复消费的问题? Mq 常见的缺点之一就是消息重复消费问题&#xff0c;产生这种问题的原因是什么呢…

VMware 安装 黑群晖7.1.1-42962 DS918+

本例的用的文件 1、ARPL 1.0beat 引导文件 vmdk格式&#xff1a; https://download.csdn.net/download/mshxuyi/88309308 2、DS918_42962.pat&#xff1a;https://download.csdn.net/download/mshxuyi/88309383 一、引导文件 1、创建一个虚拟机 2、下一步&#xff0c;选稍后…