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)将其改回原来的名字。
  • 测试。

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot表现层数据一致性

1.定义Restful类 说明:使用Data注解是Lombok库提供的一个注解,用于自动生成类的getter、setter、equals、hashcode和toString方法。 package com.forever.controller.utils;import lombok.Data;Data public class Restful {private Boolean flag;//dat…

Redis 常用命令

目录 全局命令 1)keys 2)exists 3) del(delete) 4)expire 5)type SET命令 GET命令 MSET 和 MGET命令 其他SET命令 计数命令 redis-cli,进入redis 最核心的命令:我们这里只是先介绍 set 和 get 最简单的操作…

Java集合(Collection、Iterator、Map、Collections)概述——Java第十三讲

前言 本讲我们将继续来讲解Java的其他重要知识点——Java集合。Java集合框架是Java编程语言中一个重要的部分,它提供了一套预定义的类和接口,供程序员使用数据结构来存储和操作一组对象。Java集合框架主要包括两种类型:一种是集合(Collection),存储一个元素列表,…

MySQL入门指南:数据库操作的基础知识

当谈到关系型数据库管理系统(RDBMS)时,MySQL无疑是最常见和广泛使用的一个。它是一个强大的工具,用于存储、管理和检索数据。在这篇博客中,我们将介绍MySQL的基本知识,包括数据库的操作、数据表的操作以及数据的增删改查~~ 目录 …

嵌入式学习笔记(27)uart stdio的移植

什么是stdio&#xff1f; &#xff08;1&#xff09;#include <stdio.h> &#xff08;2&#xff09;stdio:standard input output &#xff08;3&#xff09;stdio是os定义的默认的输入和输出通道。一般在PC机的情况下&#xff0c;标准输入指的是键盘&#xff0c;标准…

VIOOVI干货分享:企业车间动作作业分析如何改善?

企业车间动作作业分析是将操作动作分解为最小的分析单位&#xff0c;我们通常称之为动素。通过对动素的定性研究&#xff0c;找出合理有效的动作&#xff0c;缩短操作时间&#xff0c;实现标准化操作。在实际应用中&#xff0c;应分析和研究操作员的各种动作&#xff0c;去除没…

深入探讨Kubernetes(K8s)在云原生架构中的关键作用和应用

文章目录 1. 容器化的应用程序管理2. 自动化扩展和负载均衡3. 容器编排和调度4. 存储管理5. 自动化滚动更新6. 多云和混合云部署7. 监控和日志8. 安全9. 社区支持和生态系统10. 未来展望案例 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1…

Nginx部署前后端分离项目(Linux)

Nginx代理前端页面、后端接口 一、前端打包二、后端打包三、Linux部署Nginx启动、暂停、重启服务器部署文件地址&#xff1a; 一、前端打包 npm run build二、后端打包 通过Maven 使用package打包 三、Linux部署 安装Nginx 安装环境 yum -y install gcc pcre pcre-devel z…

SpringMVC系列(七)之自定义注解

目录 一. Java注解简介 1.1 Java注解分类 1.2 JDK基本注解 Override Deprecated SuppressWarnings 1.3 JDK元注解 从 Java 7 开始&#xff0c;额外添加了 3 个注解: 1.4 自定义注解 如何自定义注解&#xff1f; 二. 自定义注解示例 枚举类&#xff1a; 示例一&…

【JUC】Java并发编程从挖坑到入土全解(一)

目录 线程基础知识 作者&#xff08;拜个神&#xff09; 什么是JUC 为什么会出现多线程 硬件方面 摩尔定律失效 软件方面 弊端 & 问题 从线程启动开始 Java线程理解以及openJDK的实现 更加底层的的C源码 线程基础知识 作者&#xff08;拜个神&#xff09; Dou…

A Mathematical Framework for Transformer Circuits—(三)

A Mathematical Framework for Transformer Circuits Two-Layer Attention-Only TransformersThree Kinds of CompositionPath Expansion of LogitsPath Expansion of Attention Scores QK CircuitAnalyzing a Two-Layer ModelInduction HeadsInduction heads的功能Induction h…

CVE-2023-1454:Jeecg-Boot SQL注入漏洞复现

文章目录 Jeecg-Boot SQL注入漏洞(CVE-2023-1454)复现0x01 前言0x02 漏洞描述0x03 影响范围0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 Jeecg-Boot SQL注入漏洞(CVE-2023-1454)复现 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事…

Windows 性能突然打鸡血,靠 Bug 修复了多年顽疾

要说 的 Bug 集中地&#xff0c;当属资源管理器。 速度缓慢、卡顿、崩溃&#xff0c;不同设备、不同版本的用户都有不同的感受。 严格来说&#xff0c;这其实是 Windows 的传统艺能&#xff0c;要完美修复可不容易。 而作为小老弟的文件资源管理器&#xff0c;时不时来个无响…

【JUC系列-06】深入理解Semaphore底层原理和基本使用

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…

VRTK4⭐二.VRTK4的项目基础配置

文章目录 &#x1f7e5; 硬件基本配置&#x1f7e7; 设置XR Plug-in Management&#x1f7e8; 添加项目Tilia&#x1f7e9; 配置项目Hierarchy &#x1f7e5; 硬件基本配置 解决使用OpenXR,HTC头显正常追踪,但手柄无法使用的问题. 问题如下: 当我们按照官方的标准流程配置完Op…

【HttpRunnerManager】搭建接口自动化测试平台实战

一、需要准备的知识点 1. linux: 安装 python3、nginx 安装和配置、mysql 安装和配置 2. python: django 配置、uwsgi 配置 二、我搭建的环境 1. Centos7 &#xff08;配置 rabbitmq、mysql 、Supervisord&#xff09; 2. python 3.6.8 &#xff08;配置 django、uwsgi&…

pcl--第四节 采样一致性算法RANSAC

RANSAC随机采样一致性算法简介 RANSAC是“RANdom SAmple Consensus”&#xff08;随机抽样共识或采样一致性&#xff09;的缩写&#xff0c;它是一种迭代方法&#xff0c;用于从包含异常值的一组数据中估计数学模型的参数。该算法由Fischler和Bolles于1981年发布。 RANSAC算法…

基于SpringBoot+微信小程序的智慧医疗线上预约问诊小程序

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 近年来&#xff0c;随…

关于Linux服务器.sh文件启动问题

问题描述 在linux服务器上使用文本编辑&#xff08;并非vim操作&#xff09;对.sh脚本文件进行修改后无法启动&#xff0c;显示’\r’识别错误等。 错误如下&#xff1a; 错误原因 因为.sh文件在经过这种编辑后格式产生了错误&#xff0c;由unix转为了doc格式&#xff0c;需…

CentOS7上从0开始搭建Zookeeper集群

CentOS7上搭建Zookeeper集群 环境准备安装jdk安装zookeeper下载zookeeper解压zookeeper修改zookeeper配置文件 搭建zookeeper集群修改zoo.cfg文件添加myid文件启动zookeeper集群 环境准备 首先你需要准备三台zookeeper&#xff08;待会会讲zookeeper的安装流程&#xff09;&am…