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.rhkb.cn/news/126006.html

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

相关文章

【Linux从入门到精通】通信 | 共享内存(System V)

本篇文章接着上篇文章通信 | 管道通信&#xff08;匿名管道 & 命名管道&#xff09;进行讲解。本篇文章的中点内容是共享内存。 文章目录 一、初识与创建共享内存 1、1 什么是共享内存 1、2 共享内存函数 1、2、1 创建共享内存 shmget 1、2、2 ftok 生成 key 1、2、3 获取共…

YOLO目标检测——红火蚂蚁识别数据集+已标注yolo格式标签下载分享

实际项目应用&#xff1a;目标检测红火蚂蚁识别数据集在农业、生态学、环境保护、城市管理和学术研究等领域都有着广泛的应用。通过准确识别和定位红火蚂蚁&#xff0c;可以帮助我们更好地了解和管理这一入侵物种&#xff0c;从而减少其对环境和经济的负面影响。数据集说明&…

一文看懂微信小程序新版隐私协议(附带弹窗组件)

一、前言 微信小程序近期又迎来了一次改革–9月15日之后如果小程序涉及调用微信的隐私接口获取用户的信息的&#xff0c;需要用户手动同意协议后才可正常调用接口&#xff0c;否则会返回报错信息。 隐私接口目前常用的有&#xff1a;手机号快捷获取、读取照片、获取用户的头像…

【爬虫】实验项目三:验证码处理与识别

目录 一、实验目的 二、实验预习提示 三、实验内容 实验要求 基本要求&#xff1a; 改进要求A&#xff1a; 改进要求B&#xff1a; 四、实验过程 基本要求 五、源码如下 六、资料 一、实验目的 部分网站可能会使用验证机制来阻止用户无效登录或者是验证用户不是用程…

自己设计CPU学习之路——基于《Xilinx FPGA应用开发》

1. 一个32组位宽为32的寄存器堆 框图 代码 regfile.h ifndef __FEGFILE_HEADER__define __REGFILE_HEADER__define HIGH 1b1define LOW 1b0define ENABLE_ 1b0define DISABLE_ 1b1define DATA_W 32define DataBus 31:0define DATA_D 32d…

socket编程

网络协议指的是计算机网络中互相通信的对等实体之间交换信息时所必须遵守的规则的集合。一般系统网络协议包括五个部分&#xff1a;通信环境&#xff0c;传输服务&#xff0c;词汇表&#xff0c;信息的编码格式&#xff0c;时序、规则和过程。 Socket是应用层和TIP/IP协议簇通…

Middleware ❀ Kafka功能与使用详解

文章目录 1. 概述1.1. 消息队列1.2. 应用场景1.3. 工作模式1.4. 基础结构1.4.1. 结构组件1.4.2. 数据同步1.4.3. ACK机制1.4.4. 分区机制1.4.4.1. 使用Partition Key写入1.4.4.2. 轮询写入 - 默认规则1.4.4.3. 指定Partition写入 1.4.5. Offset偏移量1.4.5.1. 消息顺序性1.4.5.…

linux系统中驱动框架基本分析

大家好&#xff0c;今天分享一篇Linux驱动软件设计思想的文章。由于文章较长&#xff0c;可以先收藏后再慢慢看。 一、Linux驱动的软件架构 1.1 出发点 为适应多种体系架构的硬件&#xff0c;增强系统的可重用和跨平台能力。 1.2 分离思想 为达到一个驱动最好一行都不改就…

Apache DolphinScheduler - 快速扩展 TaskPlugin 从入门到放弃

目前在大数据生态中&#xff0c;调度系统是不可或缺的一个重要组件。Apache DolphinScheduler 作为一个顶级的 Apache 项目&#xff0c;其稳定性和易用性也可以说是名列前茅的。而对于一个调度系统来说&#xff0c;能够支持的可调度的任务类型同样是一个非常重要的因素&#xf…

国内访问香港服务器选择什么路线?

​  国内访问香港服务器可以选择多种路线。首先&#xff0c;我们了解下各个线路的速度延迟。 一、CN2直连&#xff1a;解决了不同互联网服务提供商之间访问的难题&#xff0c;不需要绕到国际网络再从中国的三个网络入口进入。 二、优化直连&#xff1a;全国平均延迟60ms&…

Ubuntu----Linux命令-----防火墙(查看、关闭、启动)

一、查看防火墙状态 命令&#xff1a;ufw status 说明&#xff1a; 活动&#xff1a;防火墙是开启的 不活动&#xff1a;防火墙是关闭的 二、开启防火墙 命令&#xff1a;sudo ufw enable 开启防火墙后&#xff0c;可以查看防火墙状态 三、关闭防火墙 命令&#xff1a;sud…

如何通过构建遥感光谱反射信号与地表参数之间的关系模型来准确估算植被参数?植被参数光学遥感反演方法(Python)及遥感与生态模型数据同化算法

目录 专题一 植被参数遥感反演理论 专题二 植被叶片及冠层反射率模拟与处理 专题三 植被遥感模型参数敏感性分析 专题四 基于查找表(LUT)方法反演植被参数 专题五 基于优化算法反演植被参数 专题六 基于机器学习反演植被参数 专题七 遥感数据同化理论 专题八 同化遥感反…

单目标应用:基于成长优化算法(Growth Optimizer,GO)的微电网优化调度MATLAB

一、微网系统运行优化模型 微电网是由分布式电源、储能装置和能量转换装置等组成的小型发配电系统&#xff0c;具有成本低、电压低、污染小等特点。由于环保和能源压力&#xff0c;清洁可再生能源和分布式能源工业发展潜力巨大。微电网控制器可实现对电网的集中控制&#xff0…

Canonical 发布公告,Ubuntu可以在 Windows 10 商店找到

导读Canonical 前几天正式发布公告称&#xff0c;“Windows 10 Loves Ubuntu”&#xff0c;其 Ubuntu 16.04 LTS 在 Windows 10 商店中以应用的方式出现&#xff0c;这是继 openSUSE 及 SLES 之后&#xff0c;又一款可以从 Windows 10 商店中下载的 Linux 操作系统。 一些用户已…

GO语言网络编程(并发编程)并发介绍,Goroutine

GO语言网络编程&#xff08;并发编程&#xff09;并发介绍&#xff0c;Goroutine 1、并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更…

Apache Linki 1.3.1+DataSphereStudio+正常启动+微服务+端口号

我使用的是一键部署容器化版本&#xff0c;官方文章 默认会启动6个 Linkis 微服务&#xff0c;其中下图linkis-cg-engineconn服务为运行任务才会启动,一共七个 LINKIS-CG-ENGINECONN:38681 LINKIS-CG-ENGINECONNMANAGER:9102 引擎管理服务 LINKIS-CG-ENTRANCE:9104 计算治理入…

Linux常用命令——convertquota命令

在线Linux命令查询工具 convertquota 把老的配额文件转换为新的格式 补充说明 convertquota命令用于将老的磁盘额数据文件&#xff08;“quota.user”和“quota.group”&#xff09;转换为新格式的文件&#xff08;“quota.user”和“quota.group”&#xff09;。 语法 c…

C/C++输出绝对值 2019年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C输出绝对值 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C输出绝对值 2019年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个浮点数&#xff0c;输出这个…

cpolar内网穿透

目录 一、引言二、什么是cpolar三、内网穿透四、如何使用cpolar1、下载cpolar软件安装包2、注册cpolar账号3、使用cpolar 一、引言 当我们完成了一个tomcat的web项目之后&#xff0c;如果我们想让其他电脑访问到这个项目&#xff0c;我们可以让其他电脑和本机连接到同一个局域…

git标签基础

打标签:git可以给仓库历史中某个提交打上标签,以示重要,比较有代表人们会使用这个功能来标记发布结点(V1.0,V2.0) 列出本地标签: git tag --list git tag -l "V1.85*" 列出远端仓库的中所有标签 git ls-remote --tags给标签添加一些描述信息 git tag -a v1.3 -m …