CH06_第一组重构(上)

news2024/12/30 2:22:15

提取函数(Extract Function |106)

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

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

在这里插入图片描述

示例代码

function printOwing(invoice) {
    printBanner();
    let outstanding = calculateOutstanding();
    //print details
    console.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)将其改回原来的名字。
  • 测试。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/982171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

git撤销还未push的的提交

怎样撤销掉上图中的提交呢 使用以下代码即可提交 git reset --soft HEAD^

如何在Ubuntu 20.04|18.04上安装 FreeSwitch

如何在Ubuntu 20.04|18.04上安装FreeSwitch 什么是 FreeSwitch PBX?FreeSwitch PBX 系统有哪些功能?开始部署部署前准备开始安装freeswitch 安装完成错误及问题FAQ常见配置文件及说明修改默认端口:防火墙配置账号密码配置/添加新用户freeswit…

如何在JavaScript中实现继承?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 原型链继承⭐ 借用构造函数继承(构造函数继承)⭐ 组合继承⭐ 原型式继承⭐ 寄生式继承⭐ 寄生组合式继承⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本…

Java+Swing+mysql高校教材管理系统

JavaSwingmysql高校教材管理系统 一、系统介绍二、功能展示1.用户登陆2.主页3.教材库管理4.出版社管理5.订单管理6.入库管理7.领书管理 三、系统实现1.salary.java 四、其它系统五、获取源码 一、系统介绍 1.实现出版社、教材类型等的管理; 2.实现教材的订购管理&…

LLM大模型推理加速 vLLM;Qwen vLLM使用案例;模型生成速度吞吐量计算

参考: https://github.com/vllm-project/vllm https://zhuanlan.zhihu.com/p/645732302 https://vllm.readthedocs.io/en/latest/getting_started/quickstart.html ##文档 1、vLLM 这里使用的cuda版本是11.4,tesla T4卡 加速原理: Paged…

STM32CubeMX驱动INA226芯片

环境 1、单片机:STM32F042F6P6 2、编译器:KeilMDK 3、配置工具:STM32CubeMX 目标 使用STM32的硬件IIC接口驱动高侧/低侧测量、双向电流/功率监视器INA226。 开始 1、配置STM32F042F6P6的IIC 2、编写INA226的驱动 头文件: #…

Windows11安装配置Git

安装配置Git 1.安装Git1.1下载Git1.2Git的安装1.安装2.安装声明3.安装路径选择4.选择安装组件5.开始菜单文件夹6.选择Git默认编辑器7.决定初始化新项目(仓库)的主干名称8.调整你的path环境变量9.选择SSH执行文件10.配置行尾符号转换11.配置终端模拟器与Git Bash一起使用12.选择…

上海亚商投顾:沪指探底回升 光刻胶概念股全线爆发

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 沪指早间弱势震荡,午后反弹拉升翻红,创业板指一度跌超1%,随后跌幅有所收窄…

算法通关村第12关【白银】| 字符串经典问题

一、反转问题 1.反转字符串 思路&#xff1a;双指针&#xff0c;反转数组一个套路 class Solution {public void reverseString(char[] s) {int l 0;int r s.length -1;while(l<r){char c s[l];s[l] s[r];s[r] c;l;r--;}} } 2.k个一组反转 思路&#xff1a;每k个进行…

noetic 怎么下载robotiq_modbus_tcp 从而使用robotiq二指夹爪

1&#xff0c;会科学上网 2&#xff0c; # Modbus TCP rosdep install robotiq_modbus_tcp3&#xff0c;报错 4&#xff0c;去robotiq的功能包里面找两个大爷&#xff08;tmd&#xff09; 分别修改package.xml 本来是python &#xff0c; 然后分别修改成python3 然后参考其他…

jpg格式图片怎么弄?不同格式图片该怎么转换?

在日常生活和工作中&#xff0c;我们经常会遇到需要转换图片格式的情况。本文将介绍如何将jpg格式图片转换为其他格式&#xff0c;以及如何转换不同格式的图片。 jpg格式图片转换的一些问题 jpg是一种常见的图片格式&#xff0c;但在某些情况下&#xff0c;需要将其转换为其他…

信息化项目软件安全测评验收

安全测试 随着信息系统业务覆盖率的提高和深度整合创新的逐步提高&#xff0c;信息系统运行阶段的复杂性和资源比例逐渐增加。一方面&#xff0c;信息已成为业务创新、技术应用和运营服务的综合体&#xff0c;而不仅仅是技术平台建设。另一方面&#xff0c;信息采购是技术平台…

Linux的LVM磁盘扩容

目录 引子环境扩容步骤结束 引子 最近因为有个服务器的磁盘和内容太小了&#xff0c;连按tab都要提示No space left on device了&#xff0c;实在受不了了&#xff0c;更何况服务器的项目也因为没有空间直接崩了&#xff0c;在领导申请到了内存和磁盘后立马着手去扩硬盘&#…

Matlab图像处理- 高斯低通滤波器

高斯低通滤波器 高斯低通滤波器是一种更平滑的一种滤波器&#xff0c;高斯低通滤波器完全没有振铃现象&#xff0c;且边缘平滑。 示例代码 利用输入图像&#xff0c;构建一个截止频率为30的高斯低通滤波器的透视图如下图所示。 M 2*size(I,1); %滤波器…

Vue进阶(贰幺幺)CVE-2020-11022/CVE-2020-11023漏洞解析

文章目录 一、前言二、漏洞原理三、修复方案3.1 升级jQuery3.2 1.x 升级至 3.x 需要考虑的问题3.2.1 table表格元素自动添加tbody3.2.2 方法变更 3.3 jquery migrate是什么 四、拓展阅读 一、前言 代码安全扫描阶段&#xff0c;前端资源审计发现jQuery版本过低导致生产系统存在…

52、基于函数式方式开发 Spring WebFlux 应用

★ Spring WebFlux的两种开发方式 1. 采用类似于Spring MVC的注解的方式来开发。此时开发时感觉Spring MVC差异不大&#xff0c;但底层依然是反应式API。2. 使用函数式编程来开发★ 使用函数式方式开发Web Flux 使用函数式开发WebFlux时需要开发两个组件&#xff1a; ▲ Han…

基于Java+SpringBoot+Vue前后端分离成绩管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

vue 脚手架新手入门(vue cli 3)

文章目录 1、vue中的 “:” 绑定和 “” 监听1.1、v-model 双向绑定 2、if 、else 、for3、computed 计算属性4、生命周期&#xff08;属性加载顺序&#xff09;5、watch 侦听器6、 components 组件6.1、props 从父组件获取收据6.2、emits 向父组件触发事件6.3、slots插槽&…

UART串口的8倍过采样和16倍过采样原理

由于在空闲状态时&#xff0c;传送线为逻辑“1”状态&#xff0c;而数据的传送总是以一个起始位“0”开始&#xff0c;所以当接收器检测到一个从"1"向"0”的跳变时&#xff0c;便视为可能的起始位&#xff08;要排除干扰引起的跳变) ;起始位被确认后,就知道发送…

java八股文面试[数据库]——JOIN优化

JOIN 是 MySQL 用来进行联表操作的&#xff0c;用来匹配两个表的数据&#xff0c;筛选并合并出符合我们要求的结果集。 JOIN 操作有多种方式&#xff0c;取决于最终数据的合并效果。常用连接方式的有以下几种: 什么是驱动表 ? 多表关联查询时,第一个被处理的表就是驱动表,使用…