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

CH06_第一组重构(上)

提取函数(Extract Function |106)

曾用名:提炼函数(Extract Function)

反向重构:内联函数(115)

在这里插入图片描述

示例代码

function printOwing(invoice) {printBanner();let outstanding = calculateOutstanding();//print detailsconsole.log(`name: ${invoice.customer}`);console.log(`amount: ${outstanding}`);
}
function printOwing(invoice) {printBanner();let outstanding = calculateOutstanding();// 提炼打印日志函数printDetails(outstanding);function printDetails(outstanding) {console.log(`name: ${invoice.customer}`);console.log(`amount: ${outstanding}`);}
}

动机

浏览一段代码,理解其作用,然后将其提炼到一个独立的函数中,并以这段代码的用途为这个函数命名。

何时应该把代码放进独立的函数(参考):

  • 一个函数应该能在一屏中显示
  • 只要被用过不止一次的代码,就应该单独放进一个函数
  • 只用过一次的代码则保持内联(inline)的状态

如果需要花时间浏览一段代码才能弄清它到底在干什么,那么就应该将其提炼到一个函数中,并根据它所做的事为其命名。以后再读到这段代码时,一眼就能看到函数的用途,大多数时候根本不需要关心函数如何达成其用途这是函数体内干的事)。

函数得有个好名字才行,所以必须在命名上花心思。起好名字需要练习,不过一旦你掌握了其中的技巧,就能写出很有自描述性的代码。

在一个大函数中,一段代码的顶上放着一句注释,说明这段代码要做什么。在把这段代码提炼到自己的函数中时,这样的注释往往会提示一个好名字。

做法

  • 创造一个新函数,根据这个函数的意图来对它命名(以它“做什么”来命名,而不是以它“怎样做”命名)。

    • 如果想不出一个更有意义的名称,这就是一个信号,可能不应该提炼这块代码。不过,不一定非得马上想出最好的名字,有时在提炼的过程中好的名字才会出现。有时会提炼一个函数,尝试使用它,然后发现不太合适,再把它内联回去,这完全没问题。

    • 如果编程语言支持嵌套函数,就把新函数嵌套在源函数里,这能减少后面需要处理的超出作用域的变量个数。

  • 将待提炼的代码从源函数复制到新建的目标函数中。

  • 仔细检查提炼出的代码,看看其中是否引用了作用域限于源函数、在提炼出的新函数中访问不到的变量。若是,以参数的形式将它们传递给新函数。

    • 如果提炼出的新函数嵌套在源函数内部,就不存在变量作用域的问题了。

    • 如果某个变量是在提炼部分之外声明但只在提炼部分被使用,就把变量声明也搬移到提炼部分代码中去。

    • 如果变量按值传递给提炼部分又在提炼部分被赋值,就必须多加小心。如果只有一个这样的变量,可以尝试将提炼出的新函数变成一个查询(query),用其返回值给该变量赋值。

    • 但有时在提炼部分被赋值的局部变量太多,这时最好是先放弃提炼。这种情况下,我会考虑先使用别的重构手法,例如拆分变量(240)或者以查询取代临时变量(178),来简化变量的使用情况,然后再考虑提炼函数。

  • 所有变量都处理完之后,编译。

  • 在源函数中,将被提炼代码段替换为对目标函数的调用。

  • 测试。

  • 查看其他代码是否有与被提炼的代码段相同或相似之处。如果有,考虑使用以函数调用取代内联代码(222)令其调用提炼出的新函数。

内联函数(Inline Method | 115)

曾用名:内联函数(Inline Method )

反向重构:提炼函数(106)

在这里插入图片描述

function getRating(driver) {return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}
function moreThanFiveLateDeliveries(driver) {return driver.numberOfLateDeliveries > 5;
}
function getRating(driver) {return (driver.numberOfLateDeliveries > 5) ? 2 : 1;
}

动机

  • 函数内部代码和函数名称同样清晰易读(简单易懂);或者重构后使其内容和其名称变得同样清晰。应该去掉这个函数,直接使用其中的代码。间接性可能带来帮助,但非必要的间接性总是让人不舒服。
  • 有一群组织不甚合理的函数,可以将它们都内联到一个大型函数中,重新提炼出小函数。
  • 如果代码中有太多间接层,使得系统中的所有函数都似乎只是对另一个函数的简单委托。

做法

  • 检查函数,确定它不具多态性(如果该函数属于一个类,并且有子类继承了这个函数,那么就无法内联)。
  • 找出这个函数的所有调用点。
  • 将这个函数的所有调用点都替换为函数本体。
  • 每次替换之后,执行测试。
  • 删除该函数的定义。

提炼变量(Extract Variable | 119)

曾用名:引入解释性变量(Introduce Explaining Variable)
反向重构:内联变量(123)

在这里插入图片描述

return order.quantity * order.itemPrice -Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +Math.min(order.quantity * order.itemPrice * 0.1, 100);
const basePrice = order.quantity * order.itemPrice;
const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
const shipping = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shipping;

动机

  • 表达式有可能非常复杂而难以阅读(拆分表达式,为每一个表达式起一个有意义的名称,有助与理解逻辑)。
  • 方便调试(可以看到各个表达式的值)。

考虑使用提炼变量,就意味着要给代码中的一个表达式命名。考虑这个名字所处的上下文,如果这个名字只在当前函数中有意义,那么提炼变量是个不错的选择。如果这个变量名在更宽的上下文中也有意义,就会考虑将其暴露出来,通常以函数的形式。

做法

  • 确认要提炼的表达式没有副作用。
  • 声明一个不可修改的变量,把想要提炼的表达式复制一份,以该表达式的结果值给这个变量赋值。
  • 用这个新变量取代原来的表达式。
  • 测试。

如果该表达式出现了多次,请用这个新变量逐一替换,每次替换之后都要执行测试。

内联变量(Inline Variable | 123)

曾用名:内联临时变量(Inline Temp)

反向重构:提炼变量(119)

在这里插入图片描述

let basePrice = anOrder.basePrice;
return (basePrice > 1000);
return (anOrder.basePrice > 1000);

动机

在一个函数内部,变量能给表达式提供有意义的名字,因此通常变量是好东西。但有时候,这个名字并不比表达式本身更具表现力。还有些时候,变量可能会妨碍重构附近的代码。若果真如此,就应该通过内联的手法消除变量。

做法

  • 检查确认变量赋值语句的右侧表达式没有副作用。
  • 如果变量没有被声明为不可修改,先将其变为不可修改,并执行测试。(确保该变量只被赋值一次)
  • 找到第一处使用该变量的地方,将其替换为直接使用赋值语句的右侧表达式。
  • 测试。
  • 重复前面两步,逐一替换其他所有使用该变量的地方。
  • 删除该变量的声明点和赋值语句。
  • 测试。

改变函数声明(Change Function Declaration | 124)

别名:函数改名(Rename Function)

曾用名:函数改名(Rename Method)

曾用名:添加参数(Add Parameter)

曾用名:移除参数(Remove Parameter)

别名:修改签名(Change Signature)

在这里插入图片描述

function circum(radius){...}
function circumference(radius){...}

动机

函数是组成软件系统的关键关节,函数声明则展示的这些关节如何在一起工作。

一个好名字能一眼看出函数的用途,而不必查看其实现代码。(有一个改进函数名字的好办法:先写一句注释描述这个函数的用途,再把这句注释变成函数的名字。)

函数的参数列表阐述了函数如何与外部世界共处。函数的参数设置了一个上下文,只有在这个上下文中,才能使用这个函数。修改参数列表不仅能增加函数的应用范围,还能改变连接一个模块所需的条件,从而去除不必要的耦合。

做法

简单做法

  • 如果想要移除一个参数,需要先确定函数体内没有使用该参数。
  • 修改函数声明,使其成为你期望的状态。
  • 找出所有使用旧的函数声明的地方,将它们改为使用新的函数声明。
  • 测试。

最好能把大的修改拆成小的步骤,所以如果既想修改函数名,又想添加参数,最好分成两步来做。

迁移式做法

  • 如果有必要的话,先对函数体内部加以重构,使后面的提炼步骤易于开展。
  • 使用提炼函数(106)将函数体提炼成一个新函数。
  • 如果提炼出的函数需要新增参数,用前面的简单做法添加即可。
  • 测试。
  • 对旧函数使用内联函数(115)。
  • 如果新函数使用了临时的名字,再次使用改变函数声明(124)将其改回原来的名字。
  • 测试。

如果要重构的函数属于一个具有多态性的类,那么对于该函数的每个实现版本,你都需要通过“提炼出一个新函数”的方式添加一层间接,并把旧函数的调用转发给新函数。

相关文章:

CH06_第一组重构(上)

提取函数(Extract Function |106) 曾用名:提炼函数(Extract Function) 反向重构:内联函数(115) 示例代码 function printOwing(invoice) {printBanner();let outstanding calcul…...

RHCSA-VMware Workstation Pro-Linux基础配置命令

1.代码命令 1.查看本机IP地址&#xff1a; ip addr 或者 ip a [foxbogon ~]$ ip addre [foxbogon ~]$ ip a 1&#xff1a;<Loopback,U,LOWER-UP> 为环回2网卡 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP>为虚拟机自身网卡 2.测试网络联通性&#xff1a; [f…...

YOLO-NAS详细教程-姿势估计实现

姿势估计是一项计算机视觉任务,涉及估计图像或视频中物体或人的位置和方向。它通常涉及识别特定的关键点或身体部位(例如关节),并确定它们的相对位置和方向。姿势估计有许多应用,包括机器人、增强现实、人机交互和运动分析。 自上而下和自下而上是姿态估计中两种常用的方法…...

【扩散模型 李宏毅B站教学以及基础代码运用】

李宏毅教学视频&#xff1a; Link1 B站DDPM公式推导以及代码实现&#xff1a; Link2 这个视频里面有论文里面的公式推导&#xff0c;并且1小时10分开始讲解实例代码。 文章目录 扩散模型概念&#xff1a;Diffusion Model工作原理&#xff1a;影像生成模型本质上的共同目标B站…...

SpringBoot隐藏文件

1.设置 2.输入file Types 3.点击忽略文件或者文件夹 4.成功...

常见数据库介绍对比之SQL关系型数据库

1.关系型数据库介绍 关系型数据库是一种基于关系模型的数据库&#xff0c;它使用表格来组织和存储数据。下面是一些常见的关系型数据库&#xff1a; 1.1. MySQL MySQL是一种开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛用于Web应用程序和企业级…...

OLED透明屏模块:引领未来显示技术的突破

OLED透明屏模块作为一项引领未来显示技术的突破&#xff0c;以其独特的特点和卓越的画质在市场上引起了广泛关注。 根据行业报告&#xff0c;预计到2025年&#xff0c;OLED透明屏模块将占据智能手机市场的20%份额&#xff0c;并在汽车导航系统市场中占据30%以上份额。 那么&am…...

Python_操作记录

1、Pandas读取数据文件&#xff08;以文本文件作为示例&#xff09;&#xff0c;sep表示间隔&#xff0c;headerNone表示无标题行 df pd.read_table("data/youcans3.dat", sep"\t", headerNone) 2、线性规划问题求解 1&#xff09;问题定义&#xff0c;…...

常用激活函数整理

最近一边应付工作&#xff0c;一边在补足人工智能的一些基础知识&#xff0c;这个方向虽然新兴&#xff0c;但已是卷帙浩繁&#xff0c;有时不知从何入手&#xff0c;幸亏有个适合基础薄弱的人士学习的网站&#xff0c;每天学习一点&#xff0c;积跬步以至千里吧。有像我一样学…...

uniapp 地图跳转到第三方导航软件 直接打包成apk

// 判断是否存在导航软件judgeHasExistNavignation() {let navAppParam [{pname: com.baidu.BaiduMap,action: baidumap://}, // 百度{pname: com.autonavi.minimap,action: iosamap://}, // 高德{pname: com.tencent.map,action: tencentmap://}, // 腾讯];return navAppPara…...

CentOS 8 通过YUM方式升级最新内核

CentOS 8 通过YUM方式升级最新内核 查看当前内核 uname -r 4.18.0-193.6.3.el8_2.x86_64导入 ELRepo 仓库的公钥&#xff1a; rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org安装升级内核相关的yum源仓库(安装 ELRepo 仓库的 yum 源) yum install https://www…...

java 版本企业招标投标管理系统源码+功能描述+tbms+及时准确+全程电子化

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…...

Python爬虫数据存哪里|数据存储到文件的几种方式

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 爬虫请求解析后的数据&#xff0c;需要保存下来&#xff0c;才能进行下一步的处理&#xff0c;一般保存数据的方式有如下几种&#xff1a; 文件&#xff1a;txt、csv、excel、json等&#xff0c;保存数据量小。 关系型数据库…...

软件测试/测试开发丨Web自动化 测试用例流程设计

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27173 一、测试用例通用结构回顾 1.1、现有测试用例存在的问题 可维护性差可读性差稳定性差 1.2、用例结构设计 测试用例的编排测试用例的项目结构 1…...

git撤销修改命令

要撤销Git中尚未提交的所有修改,可以使用以下几种方法: 1、使用git checkout命令丢弃工作目录的修改&#xff0c;重置工作目录中所有文件的修改。 git checkout . 2、使用git reset命令重置暂存区和工作目录&#xff0c; 重置暂存区和工作目录,回到最后一次提交后的状态。 …...

EOCR-AR电机保护器自动复位的启用条件说明

为适用不同的现场使用需求&#xff0c;施耐德韩国公司推出了带有自动复位功能的模拟型电动机保护器-EOCR-AR。EOCR-AR电机保护器具有过电流、缺相、堵转保护功能&#xff0c;还可根据实际需要设置自动复位时间。 EOCR-AR自动复位的设置方法 如上图&#xff0c;R-TIME旋钮是自动…...

Apache nginx解析漏洞复现

文章目录 空字节漏洞安装环境漏洞复现 背锅解析漏洞安装环境漏洞复现 空字节漏洞 安装环境 将nginx解压后放到c盘根目录下&#xff1a; 运行startup.bat启动环境&#xff1a; 在HTML文件夹下有它的主页文件&#xff1a; 漏洞复现 nginx在遇到后缀名有php的文件时&#xff0c;…...

.NET之后,再无大创新

回想起来&#xff0c;2001年发布的.NET已经是距离最近的一次软件开发技术的整体创新了&#xff0c;后续的新技术就没有在各个端都这么成功的了。.NET是Windows平台下软件开发技术的巨大变革。在此之前&#xff0c;有VB、C&#xff08;MFC&#xff09;、JSP&#xff0c;在此之后…...

【大麦小米学量化】什么是量化交易?哪些人适合做量化交易?

系列文章目录 文章目录 系列文章目录学霸的梦想前言一、什么是量化交易&#xff1f;二、哪些人适合做量化交易&#xff1f;三、量化交易都需要掌握哪些技术和方法&#xff1f;总结 学霸的梦想 小米支棱着迷糊的眼睛&#xff0c;一脸懵逼的问大麦&#xff1a;“我说大麦哥哥&…...

计算机视觉的应用12-卷积神经网络中图像特征提取的可视化研究,让大家理解特征提取的全过程

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用12-卷积神经网络中图像特征提取的可视化研究&#xff0c;让大家理解特征提取的全过程。 要理解卷积神经网络中图像特征提取的全过程&#xff0c;我们可以将其比喻为人脑对视觉信息的处理过程。就像…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...