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地址: ip addr 或者 ip a [foxbogon ~]$ ip addre [foxbogon ~]$ ip a 1:<Loopback,U,LOWER-UP> 为环回2网卡 2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP>为虚拟机自身网卡 2.测试网络联通性: [f…...
YOLO-NAS详细教程-姿势估计实现
姿势估计是一项计算机视觉任务,涉及估计图像或视频中物体或人的位置和方向。它通常涉及识别特定的关键点或身体部位(例如关节),并确定它们的相对位置和方向。姿势估计有许多应用,包括机器人、增强现实、人机交互和运动分析。 自上而下和自下而上是姿态估计中两种常用的方法…...
【扩散模型 李宏毅B站教学以及基础代码运用】
李宏毅教学视频: Link1 B站DDPM公式推导以及代码实现: Link2 这个视频里面有论文里面的公式推导,并且1小时10分开始讲解实例代码。 文章目录 扩散模型概念:Diffusion Model工作原理:影像生成模型本质上的共同目标B站…...
SpringBoot隐藏文件
1.设置 2.输入file Types 3.点击忽略文件或者文件夹 4.成功...
常见数据库介绍对比之SQL关系型数据库
1.关系型数据库介绍 关系型数据库是一种基于关系模型的数据库,它使用表格来组织和存储数据。下面是一些常见的关系型数据库: 1.1. MySQL MySQL是一种开源的关系型数据库管理系统(RDBMS),广泛用于Web应用程序和企业级…...
OLED透明屏模块:引领未来显示技术的突破
OLED透明屏模块作为一项引领未来显示技术的突破,以其独特的特点和卓越的画质在市场上引起了广泛关注。 根据行业报告,预计到2025年,OLED透明屏模块将占据智能手机市场的20%份额,并在汽车导航系统市场中占据30%以上份额。 那么&am…...
Python_操作记录
1、Pandas读取数据文件(以文本文件作为示例),sep表示间隔,headerNone表示无标题行 df pd.read_table("data/youcans3.dat", sep"\t", headerNone) 2、线性规划问题求解 1)问题定义,…...
常用激活函数整理
最近一边应付工作,一边在补足人工智能的一些基础知识,这个方向虽然新兴,但已是卷帙浩繁,有时不知从何入手,幸亏有个适合基础薄弱的人士学习的网站,每天学习一点,积跬步以至千里吧。有像我一样学…...
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 仓库的公钥: rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org安装升级内核相关的yum源仓库(安装 ELRepo 仓库的 yum 源) yum install https://www…...
java 版本企业招标投标管理系统源码+功能描述+tbms+及时准确+全程电子化
功能描述 1、门户管理:所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含:招标公告、非招标公告、系统通知、政策法规。 2、立项管理:企业用户可对需要采购的项目进行立项申请,并提交审批,查看所…...
Python爬虫数据存哪里|数据存储到文件的几种方式
前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 爬虫请求解析后的数据,需要保存下来,才能进行下一步的处理,一般保存数据的方式有如下几种: 文件:txt、csv、excel、json等,保存数据量小。 关系型数据库…...
软件测试/测试开发丨Web自动化 测试用例流程设计
点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/27173 一、测试用例通用结构回顾 1.1、现有测试用例存在的问题 可维护性差可读性差稳定性差 1.2、用例结构设计 测试用例的编排测试用例的项目结构 1…...
git撤销修改命令
要撤销Git中尚未提交的所有修改,可以使用以下几种方法: 1、使用git checkout命令丢弃工作目录的修改,重置工作目录中所有文件的修改。 git checkout . 2、使用git reset命令重置暂存区和工作目录, 重置暂存区和工作目录,回到最后一次提交后的状态。 …...
EOCR-AR电机保护器自动复位的启用条件说明
为适用不同的现场使用需求,施耐德韩国公司推出了带有自动复位功能的模拟型电动机保护器-EOCR-AR。EOCR-AR电机保护器具有过电流、缺相、堵转保护功能,还可根据实际需要设置自动复位时间。 EOCR-AR自动复位的设置方法 如上图,R-TIME旋钮是自动…...
Apache nginx解析漏洞复现
文章目录 空字节漏洞安装环境漏洞复现 背锅解析漏洞安装环境漏洞复现 空字节漏洞 安装环境 将nginx解压后放到c盘根目录下: 运行startup.bat启动环境: 在HTML文件夹下有它的主页文件: 漏洞复现 nginx在遇到后缀名有php的文件时,…...
.NET之后,再无大创新
回想起来,2001年发布的.NET已经是距离最近的一次软件开发技术的整体创新了,后续的新技术就没有在各个端都这么成功的了。.NET是Windows平台下软件开发技术的巨大变革。在此之前,有VB、C(MFC)、JSP,在此之后…...
【大麦小米学量化】什么是量化交易?哪些人适合做量化交易?
系列文章目录 文章目录 系列文章目录学霸的梦想前言一、什么是量化交易?二、哪些人适合做量化交易?三、量化交易都需要掌握哪些技术和方法?总结 学霸的梦想 小米支棱着迷糊的眼睛,一脸懵逼的问大麦:“我说大麦哥哥&…...
计算机视觉的应用12-卷积神经网络中图像特征提取的可视化研究,让大家理解特征提取的全过程
大家好,我是微学AI,今天给大家介绍一下计算机视觉的应用12-卷积神经网络中图像特征提取的可视化研究,让大家理解特征提取的全过程。 要理解卷积神经网络中图像特征提取的全过程,我们可以将其比喻为人脑对视觉信息的处理过程。就像…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
Java中栈的多种实现类详解
Java中栈的多种实现类详解:Stack、LinkedList与ArrayDeque全方位对比 前言一、Stack类——Java最早的栈实现1.1 Stack类简介1.2 常用方法1.3 优缺点分析 二、LinkedList类——灵活的双端链表2.1 LinkedList类简介2.2 常用方法2.3 优缺点分析 三、ArrayDeque类——高…...
