设计模式4-模版方法
设计模式
- 重构获得模式
- 重构的关键技法
- 1. 静态转动态
- 2. 早绑定转晚绑定
- 3. 继承转组合
- 4. 编译时依赖转运行时依赖
- 5. 紧耦合转松耦合
- 组件协助
- 动机
- 模式定义
- 结构
- 要点总结。
- 例子
- 示例解释:
重构获得模式
设计模式的目的是应对变化,提高复用
设计模式的要点是寻找变化点,然后在变化点处应用设计模式。从而更好地应对需求的变化。什么时候什么地点应用设计模式。比设计模式结构本身更为重要。
设计模式的应用,不应当先入为主,一上来就套用设计模式这是设计模式的最大误用,没有一步到位的设计。敏捷软件开发实践提倡的是refacroring to Patterns(重构), 是目前普遍公认的最好使用的设计模式的方法。
推荐书籍
重构的关键技法
让变化可配置
重构是改善现有代码设计的过程,通过一系列的技术手法和策略,使得代码更易于理解、扩展和维护。以下是几种重构的关键技法,以及它们在代码中的应用示例:
1. 静态转动态
技法说明:
- 将在编译时确定的部分改为在运行时确定,增加代码的灵活性和适应性。
示例:
- 将硬编码的条件判断改为通过配置文件或者用户输入来确定。
// 静态
bool isFeatureEnabled() {return true; // 静态确定特性总是启用
}// 动态
bool isFeatureEnabled() {// 通过配置文件或者用户输入来确定特性是否启用return config.isEnabled("featureX");
}
2. 早绑定转晚绑定
技法说明:
- 将函数调用的绑定从编译时确定延迟到运行时,使得对象能够在运行时选择调用不同的方法或策略。
示例:
- 使用虚函数和多态来实现晚绑定,而不是通过静态绑定的方式。
// 早绑定
class Shape {
public:void draw() {// 绘制代码}
};// 晚绑定
class Shape {
public:virtual void draw() {// 绘制代码}
};class Circle : public Shape {
public:void draw() override {// 绘制圆形的代码}
};class Rectangle : public Shape {
public:void draw() override {// 绘制矩形的代码}
};void render(Shape* shape) {shape->draw(); // 晚绑定,根据实际对象类型调用对应的draw方法
}
3. 继承转组合
技法说明:
- 通过组合现有类的实例来代替继承,以减少类之间的耦合性,同时提高代码的灵活性和可维护性。
示例:
- 将继承关系改为对象组合关系。
// 继承
class Car {
public:virtual void drive() {// 驾驶汽车}
};class SportsCar : public Car {
public:void drive() override {// 驾驶运动汽车}
};// 组合
class Engine {
public:void start() {// 启动引擎}
};class Car {
private:Engine engine;public:void drive() {engine.start();// 驾驶汽车}
};class SportsCar {
private:Engine engine;public:void drive() {engine.start();// 驾驶运动汽车}
};
4. 编译时依赖转运行时依赖
技法说明:
- 将程序依赖关系从编译时硬编码转为运行时根据条件动态确定,提高程序的灵活性和可配置性
示例:
- 使用依赖注入或者配置来动态确定依赖。
// 编译时依赖
class Database {
public:void saveData(const std::string& data) {// 保存数据到 MySQL 数据库}
};// 运行时依赖
class Database {
public:virtual void saveData(const std::string& data) = 0;
};class MySQLDatabase : public Database {
public:void saveData(const std::string& data) override {// 保存数据到 MySQL 数据库}
};class PostgresDatabase : public Database {
public:void saveData(const std::string& data) override {// 保存数据到 PostgreSQL 数据库}
};void saveData(Database* db, const std::string& data) {db->saveData(data); // 根据运行时传入的数据库对象调用相应的方法
}
5. 紧耦合转松耦合
技法说明:
- 减少模块或者类之间的依赖关系,使得各个模块之间可以独立开发、测试和部署。
示例:
- 使用依赖注入、接口抽象等手段降低模块间的耦合度。
// 紧耦合
class ServiceA {
public:void doSomething() {ServiceB b;b.doSomethingElse();}
};class ServiceB {
public:void doSomethingElse() {// 实现}
};// 松耦合
class ServiceB {
public:void doSomethingElse() {// 实现}
};class ServiceA {
private:ServiceB& b;public:ServiceA(ServiceB& b) : b(b) {}void doSomething() {b.doSomethingElse();}
};
通过这些关键技法,可以更好地进行代码重构,改进现有设计,使得代码更易于维护、扩展和重用,同时提高系统的灵活性和可靠性。
组件协助
定义:现代软件分专业分工之后的第一个结果是框架与应用的区分组建协作模式,通过完绑定来实现框架与应用间的松耦合是二者之间协作时常用的模式。
典型模式
• Template Method
• Observer / Event
• Strategy
动机
在软件构建过程中,每一项任务它常常有稳定的整体操作结构。但各个子步骤却有很多变化的需求。或者由于固定的原因,比如框架与应用之间的关系。而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚七实现需求?
结构化软件设计流程
面向对象软件设计流程
早绑定与晚绑定
模式定义
定义一个操作中的算法框架(稳定),而将一些步骤延迟(变化)到子类中template Method 使得子类可以不改变。一个算法的结构即可重新定义就是重写该算法的某些特定步骤。
结构
要点总结。
模板方法模式是一种非常基础性的设计模式。在面向对象系统中有着大量的应用。他用最简洁的机制也就是虚函数的多态性。为很多应用程序架构提供了灵活的扩展点。是代码复用方面的基本实现结构
除了可以灵活应对自步骤的变化以外,不要调用我,让我来调用你。反向控制结构是模板方法的典型应用。
在具体实验方面,被模板方法调用的虚方法可以具有实现,也可以没有任何实现。也就是所谓的抽象方法和纯虚方法。但一般推荐将他们设置为保护方法
例子
Template Method(模板方法)模式是一种行为设计模式,定义了一个操作中的算法的框架,将一些步骤推迟到子类中实现。这种模式是通过创建一个模板方法,该方法在一个算法的结构中定义了算法的骨架,而将一些步骤的实现延迟到子类中。
以下是一个简单的 C++ 示例,演示了模板方法模式的实现:
#include <iostream>// 抽象基类
class AbstractClass {
public:// 模板方法,定义了算法的骨架void templateMethod() {operation1();commonOperation(); // 模板方法中可以包含具体实现operation2();if (hookMethod()) { // 钩子方法,可选覆盖optionalOperation();}}protected:// 纯虚函数,需要在子类中实现virtual void operation1() = 0;virtual void operation2() = 0;// 具体方法,子类共享的实现void commonOperation() {std::cout << "AbstractClass: commonOperation" << std::endl;}// 钩子方法,可选覆盖virtual bool hookMethod() {return true; // 默认实现}// 可选的具体方法,子类可以选择性地覆盖virtual void optionalOperation() {std::cout << "AbstractClass: optionalOperation" << std::endl;}
};// 具体子类实现
class ConcreteClass1 : public AbstractClass {
protected:void operation1() override {std::cout << "ConcreteClass1: operation1" << std::endl;}void operation2() override {std::cout << "ConcreteClass1: operation2" << std::endl;}bool hookMethod() override {std::cout << "ConcreteClass1: hookMethod" << std::endl;return false; // 覆盖钩子方法}
};// 另一个具体子类
class ConcreteClass2 : public AbstractClass {
protected:void operation1() override {std::cout << "ConcreteClass2: operation1" << std::endl;}void operation2() override {std::cout << "ConcreteClass2: operation2" << std::endl;}// 这里没有覆盖钩子方法
};// 客户端代码
int main() {AbstractClass* object1 = new ConcreteClass1();AbstractClass* object2 = new ConcreteClass2();// 调用模板方法,执行算法object1->templateMethod();std::cout << std::endl;object2->templateMethod();delete object1;delete object2;return 0;
}
示例解释:
-
AbstractClass 是抽象基类,定义了模板方法
templateMethod()
和一些具体方法commonOperation()
、optionalOperation()
,以及一个钩子方法hookMethod()
。 -
ConcreteClass1 和 ConcreteClass2 是具体子类,分别实现了抽象类中的纯虚函数
operation1()
和operation2()
,并选择性地覆盖了钩子方法hookMethod()
。 -
在客户端代码中,创建了
ConcreteClass1
和ConcreteClass2
的对象,并调用它们的templateMethod()
方法,执行了定义好的算法。 -
输出结果展示了每个步骤的执行顺序,包括了钩子方法的选择性覆盖情况。
这个例子展示了模板方法模式的核心思想:定义一个算法的骨架,将特定的步骤延迟到子类中去实现,从而在不同的子类中实现算法的不同部分,同时保持算法结构的稳定性。
相关文章:

设计模式4-模版方法
设计模式 重构获得模式重构的关键技法1. 静态转动态2. 早绑定转晚绑定3. 继承转组合4. 编译时依赖转运行时依赖5. 紧耦合转松耦合 组件协助动机模式定义结构 要点总结。 例子示例解释: 重构获得模式 设计模式的目的是应对变化,提高复用 设计模式的要点…...

yii2 ActiveForm使用技巧
持续更新: 1、搜索输入框:form-inline <?php $form ActiveForm::begin([action > [index],method > get,options > [class > form-inline] (增加此行代码) ]); ?>...
【面试】基本数据类型的包装类缓存
目录 1. 说明2. Integer类分析2.1 代码块2.2 字节码2.3 分析2.4 valueOf方法 1. 说明 1.在java中,基本数据类型的包装类(Integer、Byte、Character、Short、Long、Boolean)的某些值会被缓存。2.以提高性能并减少内存使用。3.这种缓存机制是自…...

6月20日(周四)A股行情总结:A股险守3000点,恒生科技指数跌1.6%
A股三大股指走弱,科创板逆势上扬,半导体板块走强,多股20CM涨停。中芯国际港股涨超1%。恒生科技指数跌超1%。离岸人民币对美元汇率小幅走低,20日盘中最低跌至7.2874,创下2023年11月中旬以来的新低,随后收复部…...

Parallels Desktop 19 for mac破解版安装激活使用指南
Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机,并于其中装设不同的操作系统,如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时,您可在 …...
JExcel API使用笔记
JExcel API使用笔记 JExcel是一个开源的支持excel的java类库,广泛利用其api来生成excel报表 API基本使用 1.创建excel文件 workbook Workbook.createWorkbook(file);//传入file文件2.创建sheet页 WritableSheet sheet workbook.createSheet("记录表&quo…...

springCloudAlibaba之分布式网关组件---gateway
gateway-网关 网关spring cloud gatewaygateway初体验gateway整合nacos简写方式 内置路由断言工厂内置断言工厂 自定义路由断言工厂自定义路由工厂 内置/自定义过滤器典型内置过滤器自定义过滤器 全局过滤器自定义全局过滤器 请求日志记录&跨域处理Gateway跨域配置…...
Springboot项目jar加密
部署的程序进行加密,防止第三方非法拷贝走项目进行二次开发或部署。我们知道java代码编译后生成的以.class结尾的字节码文件或者.jar/.war结尾的可执行文件都是可以反编译生成.java文件的,虽然反编译后生成的.java文件和原本的.java文件有些微差别&#…...
【React】高阶组件
概述 高阶组件并非一个组件,而是增强组件功能的一个函数。 高阶组件的作用是对多个组件公共逻辑进行横向抽离。 高阶组件 – React (reactjs.org) 示例 ChildCom1.jsx import React from react;function ChildCom1(props) {return (<div>这是子组件1<d…...

全面理解-Flutter(万字长文,深度解析)
1、Web 性能差,跟原生 App 存在肉眼可见的差距; 2、React Native 跟 Web 相比,支持的能力非常有限,特定长场景问题,需要三端团队一个一个处理; 3、Web 浏览器的安卓碎片化严重(感谢 X5&#x…...
RabbitMQ实战宝典:从新手到专家的全面探索
前言 在当今分布式系统架构中,消息队列已成为不可或缺的一部分,而RabbitMQ作为其中的佼佼者,凭借其强大的功能和灵活性,广泛应用于各种规模的应用场景中。本文将带你从基础概念出发,深入探讨RabbitMQ的核心特性&#…...

6月21日(周五)AH股总结:沪指失守3000点,恒生科技指数跌近2%,多只沪深300ETF午后量能显著放大
内容提要 沪指全天围绕3000点关口来回拉锯,收盘跌破3000点。白酒及光刻机概念集体走低,中芯国际港股跌超2%。CRO医药概念及水利股逆势走强。 A股低开低走 沪指全天围绕3000点关口来回拉锯,收盘跌破3000点,跌0.24%。深成指跌0.04…...

双非本,3年时间从外包到阿里P6(Android岗),看我是怎么逆袭成功的?
而在小公司,因为我也在小公司呆过,所以我有最直接的感受。整个部门技术人员没几个,我又大学刚毕业,带我的人,问啥啥不会,只有一个大佬,跳槽来的,是我们技术总监,有问题谁…...
前端面试题(基础篇七)
一、谈谈你对webpack的看法 webpack是一个模块打包工具,我们可以使用webpack管理我们的模块依赖,编译输出模块所需的静态文件。它可以很好的管理、打包web开发中所需的html、css、JavaScript以及其他各种静态文件(使用的图片、字体图标等&am…...

ARM架构简明教程
目录 一、ARM架构 1、RISC指令集 2、ARM架构数据类型的约定 2.1 ARM-v7架构数据类型的约定 2.2 ARM-v8架构数据类型的约定 3、CPU内部寄存器 4、特殊寄存器 4.1 SP寄存器 4.2 LR寄存器 4.3 PC寄存器 二、汇编 1、汇编指令(常用) 2、C函数的…...

DWG转PDF字体研究记录
1.前言 最近需要对PDF中的符合业务规则的文字进行提取,发现有些文字不是文字信息形式存储,而是polyline形式表达,意味着仅仅有形体上的表达,丢失了原本的文字信息。 经过沟通得知,这些PDF是AutoCAD软件导出的…...
Java中如何处理日期和时间?
Java中如何处理日期和时间? 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何在Java中处理日期和时间,这是开发中非常常…...
Kubernetes之Pod详解
如何更好的使用好Pod?本文尝试从Pod组成、Namespace共享、控制器实现原理及Pod设计原则4个方面对Pod的使用进行详细阐述,希望对您 一、 Kubernetes Pod介绍 在 Kubernetes 中,Pod 是最小的可部署单元,包含一个或多个容器。Pod 提…...

长亭谛听教程部署和详细教程
PPT 图片先挂着 挺概念的 谛听的能力 hw的时候可能会问你用过的安全产品能力能加分挺重要 溯源反制 反制很重要感觉很厉害 取证分析 诱捕牵制 其实就是蜜罐 有模板直接爬取某些网页模板进行伪装 部署要求 挺低的 对linux内核版本有要求 需要root 还有系统配置也要修改 …...
修复漏洞Windows 2012 Server R2(CVE-2016-2183)、(CVE-2015-2808)、(CVE-2013-2566)
修复漏洞 漏洞风险等级评定标准主机风险等级评定标准漏洞概括利用注册表修复漏洞查看修复后的漏洞漏洞风险等级评定标准 危险程度危险值区域危险程度说明高7 <=漏洞风险值<= 10攻击者可以远程执行任意命令或者代码,或对系统进行远程拒绝服务攻击。中4 <=漏洞风险值&l…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...