CH06_第一组重构(上)

这篇具有很好参考价值的文章主要介绍了CH06_第一组重构(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

提取函数(Extract Function |106)

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

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

CH06_第一组重构(上),读《重构改善既有代码的设计第2版》,重构

示例代码

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)

CH06_第一组重构(上),读《重构改善既有代码的设计第2版》,重构

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)

CH06_第一组重构(上),读《重构改善既有代码的设计第2版》,重构

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)

CH06_第一组重构(上),读《重构改善既有代码的设计第2版》,重构

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)

CH06_第一组重构(上),读《重构改善既有代码的设计第2版》,重构

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

动机

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

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

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

做法

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

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

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

如果要重构的函数属于一个具有多态性的类,那么对于该函数的每个实现版本,你都需要通过“提炼出一个新函数”的方式添加一层间接,并把旧函数的调用转发给新函数。文章来源地址https://www.toymoban.com/news/detail-699600.html

到了这里,关于CH06_第一组重构(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CH01_重构、第一个示例

    在这一章节,作者给出了一个戏剧演出团售票的示例:剧目有悲剧(tragedy)和喜剧(comedy);为了卖出更多的票,剧团则更具观众的数量来为下次演出打折扣(大致意思是这次的观众越多,下次的票价越低) 采用JSON存储数据(因为代码是用Javascript写的),plays.json 存储剧目

    2024年02月12日
    浏览(28)
  • 2022《人工智能》_ch06

    题目 如图所示的地图着色问题共有多少个解?如果是四色有多少个解?如果只有两色呢? 三色 依据MCV对节点排序, SA NT Q NSW WA V T 1 ◯ bigcirc ◯ × times × × times × × times × × times × × times × ◯ bigcirc ◯ 2 ◯ bigcirc ◯ × times × ◯ bigcirc ◯ × times × × times × 3 ◯ bigc

    2023年04月19日
    浏览(32)
  • 【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性

    其实之前的工作中强调过很多次自己做测试的重要性,例如讲单元测试的: 【C#编程最佳实践 一】单元测试实践 ,讲单元测试规范的 【阿里巴巴Java编程规范学习 四】Java质量安全规约 ,讲接口测试的: 【C#编程最佳实践 十三】接口测试实践 ,这里旧事重提就不再详细展开

    2023年04月25日
    浏览(64)
  • 关于UICollectionView,第一组cell给于0高度,直接动态给第二组添加cell就会出的奇葩问题

    大概意思就是第一组没有数据就直接将改组的cell高度变成0 效果实现了,但是第二组数据创建cell就出问题了--奇葩问题 * 代码问题在这 ``` -(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{            

    2024年02月13日
    浏览(33)
  • 【设计模式之美】重构(三)之解耦方法论:如何通过封装、抽象、模块化、中间层等解耦代码?

    重构可以分为大规模高层重构(简称“大型重构”)和小规模低层次重构(简称“小型重构”)。 通过解耦对代码重构,就是保证代码不至于复杂到无法控制的有效手段。   代码是否需要“解耦”? 看修改代码会不会牵一发而动全身。 依赖关系是否复杂 把模块与模块之间

    2024年01月16日
    浏览(54)
  • 分享一组有意思的按钮设计

    先上效果图: 一共16个,每个都有自己不同的样式和效果,可以用在自己的项目中,提升客户体验~ 再上代码:

    2024年02月04日
    浏览(44)
  • 用 AIGC 重构后的智能客服,能否淘到大模型时代的第一桶金?

    ChatGPT 的诞生打响了现代 AI 军备竞赛的第一枪。以 GPT-4、ChatGTP、Bard 等为代表的大语言模型在全球各界引起了广泛关注。结合 ChatGPT 的底层技术逻辑,未来中短期内 ChatGPT 产业化的方向大致有四类:即智能客服、文字模态的 AIGC 应用、代码开发相关工作以及图像生成。其中,

    2024年02月16日
    浏览(46)
  • 《Effective C++ 改善程序与设计的55个具体做法》读书笔记

    条款01 视C++为一个语言联邦 C Object-Oriented C++ Template C++ STL C++ 高效编程守则视情况而变化,取决于你使用 C++ 的哪一部分。 条款02 尽量与const,enum,inline替换#define 对于单纯常量,最好以 const 对象或 enums 替换 #defines 。 对于形似函数的宏( macros ),最好改用 inline 函数替换

    2024年02月12日
    浏览(34)
  • C语言程序设计:用do while 语句实现从键盘输入一组整数,求平均值

    题目内容: 用do while 语句实现反复从键盘输入一个整数,如果大于或等于零,累加求和,直到输入负数结束。然后求出平均值并输出。 输入格式: \\\"%d\\\" 输出格式: \\\"AVE=%.2fn\\\" 输入样例: 25 35 45 55 -1 输出样例: AVE=40.00 时间限制:500ms内存限制:32000kb

    2024年02月07日
    浏览(71)
  • 【配电网重构】基于混合整数二阶锥配电网重构研究(Matlab代码实现)

    💥💥💞💞 欢迎来到本博客 ❤️❤️💥💥 🏆博主优势: 🌞🌞🌞 博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️ 座右铭: 行百里者,a半于九十。 📋📋📋 本文目录如下: 🎁🎁🎁 目录 💥1 概述 📚2 运行结果 🎉3 参考文献 🌈4 Matlab代码实现 随着经

    2024年02月07日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包