当前位置: 首页 > news >正文

【组件协作】模板方法

文章目录

  • 模板方法
    • 总体划分
    • 重构
    • 组件协作模式——模板方法
      • 动机
      • 代码——做法一
      • 代码——做法二
      • 对比
      • 定义
      • 缺点
      • 总结
    • 其他

模板方法

总体划分

设计模式的总体分类:
目的:

  • 创建型:解决对象创建的工作
  • 结构型:对象在需求变化对结构的冲击
  • 行为型: 多个类交互的责任的划分

范围:

  • 类模式处理类与子类的静态关系
  • 对象模式处理对象间的动态关系

从封装变化角度:

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

重构

书籍推荐:

  • 《重构——改善既有代码的设计》
  • 《重构与模式》

设计模式的应用并非一蹴而就, 而是不断的重构, 其中的关键技法:

  • 静态->动态
  • 早绑定->晚绑定
  • 继承->组合
  • 编译时依赖->运行时依赖
  • 紧耦->松耦合

组件协作模式——模板方法

现代软件专业分工之后的第一个结果就是:框架和应用程序
组件协作模式通过晚绑定, 来实现框架与应用之间的松耦合

典型代表:

  • Template Method
  • Strategy
  • Observer/Event

当然, 并不是说其他的设计模式与组件协作无关, 而是想表达:这些模式体现明显

动机

在软件构建中, 常常有稳定的整体框架, 但各个子不走有很多改变的需求, 或者由于固有的原因
比如框架和应用之间的关系, 而无法和任务的整体结构同时实现

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

代码——做法一

库:
lib.cpp

class Library
{
public:void Step1(){// code}void Step3(){// code}void Step5(){// code}
};

应用层:

class Application
{
public:bool Step2(){// code}void Step4(){// code}
}// 模拟的业务逻辑
int main()
{Library lib;Application app;lib.Step1();if (app.Step2()){lib.Step3();}for (size_t i = 0; i < 4; ++ i){app.Step4();}lib.Step5();return 0;
}

代码——做法二

库:
lib.cpp

class Library
{
public:// 稳定 template methodvoid Run(){Step1();if (Step2()){// 支持变化->虚函数多态调用Step3();}for (size_t i = 0; i < 4; ++ i){// 支持变化->虚函数多态调用Step4();}Step5();}virtual ~Library(){// code}protected:virtual void Step1(){// 稳定// code}virtual void Step3(){// 稳定// code}virtual void Step5(){// 稳定// code}virtual bool Step2() = 0;   // 变化virtual void Step4() = 0;   // 变化
}

应用程序开发:
main.cpp

class Application : public Library
{
protected:virtual bool Step2(){// 子类的重写实现}virtual void Step4(){// 子类的重写实现}
};int main()
{Library *lib = new Application();lib->Run();delete lib;return 0;
}

对比

对于写法一
业务有五个步骤:
lib库完成1, 3, 5
业务开发人员完成:2, 4和程序主体

对于做法二:
lib库仍然完成1, 3, 5
但是lib库开发多完成了一个程序主流程

而Application开发人员完成:2, 4

调用关系:
方法一:Application开发人员调用库函数
方法二:Application开发人员调用程序主体(虚函数)

那么可以看出:
方法一:早绑定的做法, 为什么是早绑定

  1. 因为库写的早, Appcation的开发晚, 一个晚的东西调用早的东西就是早绑定

早绑定一直是面向过程时期的做法, 但是在面向对象这, 就有了晚绑定
虽然lib仍然是写的早, Application开发仍然是晚
但是lib反过来调用了Application, 也就是早的东西调用晚东西, 这个就叫晚绑定

因此就可以引出模板方法的定义:

定义

定义一个操作中的算法的骨架(稳定, 上面demo的Run方法), 而将一些步骤延迟(变化)到子类中。
Template Method使得子类可以不改变(复用)一个算法的结构即可实现重定义(override重写)该算法的某些步骤

缺点

这个做法的Run必须得是稳定的, 因此如果Run不稳定, 则此设计模式不适用
这也是Template Method定义中提到的:一个操作中算法的骨架

那么还有一个有趣的点:
假设Step2和Step4都是稳定, 那么Template Method就没必要使用了, 反正都是稳定的
根本原因是因为设计模式的核心是在变化和稳定之间寻找分界点, 来分离和管理变化
将变化关在一个笼子里, 任由其在笼子里蹦跶, 但是不影响整个系统

总结

那么回过头来思考一下, 为什么要把程序的主体流程放在lib库里完成?
答案呼之欲出, 因为要稳定

但是这样也有弊端:
用方法一:业务开发人员不得不完成主流程, 其业务水平会得到很大的提升, 因为你不完成, 整个Application就无法实现
方法二:核心在父类里, 而业务开发人员不用写主流程, 甚至只要override几个虚函数就可以了

因此如果你是方法二的业务开发人员, 你会有一种只见树木不见森林的感觉, 因为你写的是子步骤,而不是核心流程

在Template Method中, 蕴含着一种“不要调用我, 我来调用你”的反向控制结构

其他

  1. 基类的析构函数要声明为虚函数
  2. 在面向对象中, 扩展一般为:继承+多态
  3. 如何实现晚绑定, 在面向对象中最基本和最基础的做法就是:虚函数, 但是在c++中还可以使用函数指针/函数对象等, 虽然虚函数的实现机制就是利用函数指针
  4. 被Template Method调用的函数可以有实现也可以没有实现, 一般声明和设计为protected

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【TS】TypeScript数组类型:掌握数据集合的类型安全
  • 2024钉钉杯A题思路详解
  • 【Qt】修改窗口的标题和图标
  • PHP西陆招聘求职系统小程序源码
  • chrome 接口请求等待时间(installed 已停止)过长问题定位
  • 实现代码灵活性:用Roslyn动态编译和执行存储在数据库中的C#代码
  • 论文解读:DiAD之SG网络
  • win 自动的杀毒软件,误报病毒情况 如何排除
  • jmeter录制
  • ESP32无线WiFi蓝牙双模方案加速设备联网创新,启明云端乐鑫代理商
  • Java小白入门到实战应用教程-switch case条件语句
  • 图片等比例缩放方案
  • 2235234234
  • 人工智能幻觉的成因分析和解决措施的挑战
  • Flink CDC基本概念以及MySQL同步到MySQL
  • python3.6+scrapy+mysql 爬虫实战
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • iOS编译提示和导航提示
  • iOS小技巧之UIImagePickerController实现头像选择
  • JavaScript-Array类型
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • KMP算法及优化
  • LeetCode29.两数相除 JavaScript
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 离散点最小(凸)包围边界查找
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 用element的upload组件实现多图片上传和压缩
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 找一份好的前端工作,起点很重要
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 函数计算新功能-----支持C#函数
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • # linux 中使用 visudo 命令,怎么保存退出?
  • #pragma once与条件编译
  • (26)4.7 字符函数和字符串函数
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (二)PySpark3:SparkSQL编程
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • (转载)Linux 多线程条件变量同步
  • .Net Core中的内存缓存实现——Redis及MemoryCache(2个可选)方案的实现
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .Net 中Partitioner static与dynamic的性能对比
  • .net中调用windows performance记录性能信息
  • @Async 异步注解使用
  • @Autowired多个相同类型bean装配问题
  • @ConfigurationProperties注解对数据的自动封装
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @Query中countQuery的介绍
  • @SpringBootApplication 注解
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [AIGC] HashMap的扩容与缩容:动态调整容量以提高性能
  • [ARM]ldr 和 adr 伪指令的区别