CH02_重构的原则(什么是重构、为什么重构、何时重构)

什么是重构

重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

重构的关键在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。

可观察行为:整体而言,经过重构之后的代码所做的事应该与重构之前大致一样。

重构与性能优化有很多相似之处:两者都需要修改代码,并且两者都不会改变程序的整体功能。两者的差别在于其目的:重构是为了让代码“更容易理解,更易于修改”。这可能使程序运行得更快,也可能使程序运行得更慢。在性能优化时,只关心让程序运行得更快,最终得到的代码有可能更难理解和维护。

两顶帽子

这是一个比喻,使用重构技术开发软件时,我把自己的时间分配给两种截然不同的行为:添加新功能和重构。添加新功能时,我们不应该修改既有代码,只管添加新功能。重构时我们就不饿能添加功能,只管调整代码的结构。

实际开发过程中,我们可能需要经常变换帽子。如果把程序结构改一下,功能的添加会容易得多;新功能添加好了以后发现代码难以理解,则继续重构。

为何重构

我们之所以重构,因为它能让我们更快——添加功能更快,修复bug更快。

重构改进软件的设计

没有重构,程序的内部设计(架构)会逐渐腐败变质。当人们只为短期目的而修改代码时,他们经常没有完全理解架构的整体设计,于是代码逐渐失去了自己的结构。程序员越来越难通过阅读源码来理解原来的设计。

经常性的重构有助于代码维持自己该有的形态。

重构使软件更容易理解

写让计算机理解的代码很容易,只要能编译通过并运行;但写出让别人能理解的代码则需要下点功夫了。所以我们写代码要考虑以后那个修改的人,而且那个人很可能是哦我们自己。

开始进行重构前,代码可以正常运行,但结构不够理想。在重构上花一点点时间,就可以让代码更好地表达自己的意图。

重构帮助找到Bug

对代码进行重构,就可以深入理解代码的所作所为,并立即把新的理解反映在代码当中。搞清楚程序结构的同时,也验证了所做的一些假设, 更容易将Bug找出来。重构能够帮助我们更有效地写出健壮的代码。

重构提高编程速度

设计耐久性假说:通过投入精力改善内部设计,我们增加了软件的耐久性,从而可以更长时间地保持开发的快速。

在这里插入图片描述

行业的陈规认为:良好的设计必须在开始编程之前完成,因为一旦开始编写代码,设计就只会逐渐腐败。重构改变了这个图景。现在我们可以改善已有代码的设计,因此我们可以先做一个设计,然后不断改善它,哪怕程序本身的功能也在不断发生着变化。由于预先做出良好的设计非常困难,想要既体面又快速地开发功能,重构必不可少。

何时重构

三次法则

  • 第一次做某件事时只管去做
  • 第二次做类似的事会产生反感,但无论如何还是可以去做
  • 第三次再做类似的事,你就应该重构。

预备性重构:让添加新功能更容易

重构的最佳时机就在添加新功能之前。例如有个函数提供了我们需要的大部分功能,只是有几个变量跟我们需要的冲突,通常我们的做法使把这个函数复制过来,修改几个值。这样做会导致重复代码(将来有可能需要修改两处)。使用函数参数化(310)进行重构后,只需要调用这个函数,传入需要的参数。

修复bug时的情况也是一样。在寻找问题根因时,可能会发现:如果把3段一模一样且都会导致错误的代码合并到一处,问题修复起来会容易得多。

帮助理解的重构:使代码更易懂

重构带来的帮助不仅发生在将来——常常是立竿见影。先在一些小细节上使用重构来帮助理解,给一两个变量改名,让它们更清楚地表达意图,以方便理解,或是将一个长函数拆成几个小函数。当代码变得更清晰一些时就会看见之前看不见的设计问题。

捡垃圾式重构

帮助理解的重构还有一个变体:已经理解代码在做什么,但发现它做得不好,例如逻辑不必要地迂回复杂,或者两个函数几乎完全相同,可以用一个参数化的函数取而代之。有两种处理方式:如果垃圾很容易重构,马上重构它;如果重构需要花一些精力,记录下来,完成当下的任务再回来重构它。

有计划的重构

一般项目计划上没有专门留给重构的时间,绝大多数重构都在做其他事的过程中自然发生。如果团队过去忽视了重构,那么常常会需要专门花一些时间来优化代码库,以便更容易添加新功能。

长久以来,人们认为编写软件是一个累加的过程:要添加新功能,我们就应该增加新代码。但优秀的程序员知道,添加新功能最快的方法往往是先修改现有的代码,使新功能容易被加入。所以,软件永远不应该被视为“完成”。每当需要新能力时,软件就应该做出相应的改变。越是在已有代码中,这样的改变就越显重要。

何时不应该重构

如果有一块凌乱的代码,但并不需要修改它,那么就不需要重构它。如果丑陋的代码能被隐藏在一个API之下,暂时容忍它继续保持丑陋。只有当需要理解其工作原理时,对其进行重构才有价值。

另一种情况是,如果重写比重构还容易,就别重构了。

重构的挑战

延缓新功能开发

很多人认为,花在重构的时间是在拖慢新功能的开发进度。“重构会拖慢进度”这种看法仍然很普遍,这可能是导致人们没有充分重构的最大阻力所在。

重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值。

有一种情况确实需要权衡取舍:有时会看到一个(大规模的)重构很有必要进行,而马上要添加的功能非常小,这时可以先把新功能加上,然后再做这次大规模重构。

测试

如果开发环境(开发工具)能很好的支持自动化重构,则可以信任这些重构;如果不能最好有完备的测试套件(测试环境、测试代码、单元测试等)。

遗留代码

遗留代码往往很复杂,可能也没有充足的测试,关键还是别人写的。如果不幸遇到,没有什么好办法:没测试就加测试、随时重构相关的代码(不建议尝试一鼓作气把复杂而混乱的遗留代码重构成漂亮的代码)。

重构、架构和YAGNI

YAGNI:“你不需要它(you are`t going to need it)”。YAGNI并不是“不做架构性思考”的意思,不过确实有人以这种欠考虑的方式做事。

重构对架构最大的影响在于,通过重构,我们能得到一个设计良好的代码库,使其能够优雅地应对不断变化的需求。“在编码之前先完成架构”这种做法最大的问题在于,它假设了软件的需求可以预先充分理解。但经验显示,这个假设很多时候甚至可以说大多数时候是不切实际的。只有真正使用了软件、看到了软件对工作的影响,人们才会想明白自己到底需要什么。

重构与软件开发过程

重构是否有效,与团队采用的其他软件开发实践紧密相关。

重构的第一块基石是自测试代码。

如果一支团队想要重构,那么每个团队成员都需要掌握重构技能,能在需要时开展重构,而不会干扰其他人的工作。

自测试代码、持续集成、重构三者之间有着很强的协同效应。

重构与性能

重构可能使软件运行更慢,但它也使软件的性能优化更容易。先写出可调优的软件,然后调优它以求获得足够的速度。

编写构造良好的程序,不对性能投以特别的关注,直至进入性能优化阶段——那通常是在开发后期。一旦进入该阶段,再遵循特定的流程来调优程序性能。

在性能优化阶段,首先应该用一个度量工具来监控程序的运行,让它告诉我程序中哪些地方大量消耗时间和空间。这样就可以找出性能热点所在的一小段代码。然后集中关注这些性能热点,并使用持续关注法中的优化手段来优化它们。

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

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

相关文章

VScode的PHP远程调试模式Xdebug

目录 第一步、安装VScode中相应插件 remote-ssh的原理 ssh插件: PHP相关插件: 第二步、安装对应PHP版本的xdebug 查看PHP具体配置信息的phpinfo页面 1、首先,打开php编辑器,新建一个php文件,例如:inde…

iOS App签名与重签名:从开发者证书到重新安装运行

前文回顾: iOS脱壳技术(二):深入探讨dumpdecrypted工具的高级使用方法 iOS逆向:越狱及相关概念的介绍 在本文中,我们将详细介绍iOS应用的签名过程,包括开发者证书的种类、证书与App ID、Provisi…

【ffmpeg】基于需要使用videocapture的opencv编译配置(C++)

目录 配置简介ffmpeg源码编译方法记录gstreamer命令行安装方法opencv的编译项记录 配置简介 opencv使用videocapture读取视频流时,需要借助底层的ffmpeg库。如果不能正确编译,会报错,现记录正确编译配置方法。 ffmpeg源码编译方法记录 ope…

一种IDEA疑难杂症的解决办法

解决办法 重启IDEA 针对于IDEA各种解析,运行时问题,但是无法通过搜索引擎得到答案的问题请试试此方法。 删除根目录下[.idea]文件夹后重启 此文件夹为idea首次导入项目时根据项目情况自动生成的配置文件。方便idea下次更快的解析项目。但是某些情况&a…

Pimpl模式

写在前面 Pimpl(Pointer to implementation,又称作“编译防火墙”) 是一种减少代码依赖和编译时间的C编程技巧,其基本思想是将一个外部可见类(visible class)的实现细节(一般是所有私有的非虚成员)放在一个单独的实现类(implemen…

用MFC打开外部程序

在MFC(Microsoft Foundation Classes)中,你可以使用ShellExecute函数来打开Notepad并加载指定的文件。ShellExecute函数是Windows API的一部分,它可以执行与操作系统相关的操作,例如打开文件、运行程序等。 以下是在M…

算法修炼Day60|● 84.柱状图中最大的矩形

LeetCode:84.柱状图中最大的矩形 84. 柱状图中最大的矩形 - 力扣(LeetCode) 1.思路 双指针思路,以当前数组为中心,借助两个数组存放当前数柱左右两侧小于当前数柱高度的索引,进行h*w的计算。注意首尾节点的左侧索引…

牛客练习赛 114

C.Kevin的七彩旗 思路:贪心和dp均可以解决。 贪心:我们可以发现,最终想要获得合法的序列,我们必须是通过把几段连续的序列拼凑起来,但序列之间可能有重合,因此我们就转化为了,记录每一段最大的…

elementuiplus设置scroll-to-error之后 提示被遮挡的解决方案

项目场景: 普通的头部固定,中间滑动的布局,中间内容有表单,提交校验不通过时滚动到第一个错误项 问题描述 elementuiplus的scroll-to-error设置之后是局部滚动 当头部内容层级高于中间表单的时候,错误会被遮挡。 ---…

【HarmonyOS】实现将pcm音频文件进行编码并写入文件(API6 Java)

【关键字】 音频编码、管道模式、createEncoder 【写在前面】 在使用API6开发HarmonyOS应用时,如何将pcm源文件进行编码并写入文件,最后生成aac文件,本文直接附上主要代码开发步骤供大家参考。 【主要功能代码】 import ohos.media.codec.…

【C++】4、Preprocessor 预处理:条件编译、源文件包含、宏替换、重定义行号、错误信息、编译器预留指令

文章目录 一、概述二、格式2.1 条件编译2.2 源文件包含2.3 宏替换2.3.1 语法2.3.2 C标准内置的预定义宏 2.4 重定义行号和文件名2.5 错误信息2.6 编译器预留指令 三、应用场景 C的 Build 可分为4个步骤:预处理、编译、汇编、链接。 预处理就是本文要详细说的宏替换…

基于Red Hat Enterprise Linux 7操作系统的PostgresSql15的备份恢复(实践笔记)

零、前言 本文是基于阿里云ECS服务器进行的实践操作,操作系统版本:Red Hat Enterprise Linux 7 PG数据库版本:PostgresSql 15 PG安装方式:yum 由于本人新接触pg数据,本次也是出于好奇,就对pg数据库的pg_du…

C#,《小白学程序》第五课:队列(Queue)

1 文本格式 /// <summary> /// 《小白学程序》第五课&#xff1a;队列&#xff08;Queue&#xff09; /// 日常生活中常见的排队&#xff0c;软件怎么体现呢&#xff1f; /// 排队的基本原则是&#xff1a;先到先得&#xff0c;先到先吃&#xff0c;先进先出 /// </su…

iOS开发Swift-枚举

枚举&#xff1a;一组相关的值定义了一个共同的类型&#xff0c;使你可以在代码中以类型安全的方式来使用这些值。 1.枚举语法 //枚举成员不会被赋予默认的整型值。成员本身就是完备的值&#xff0c;类型为CompassPoint。 enum CompassPoint {case northcase southcase eastcas…

深度学习8:详解生成对抗网络原理

目录 大纲 生成随机变量 可以伪随机生成均匀随机变量 随机变量表示为操作或过程的结果 逆变换方法 生成模型 我们试图生成非常复杂的随机变量…… …所以让我们使用神经网络的变换方法作为函数&#xff01; 生成匹配网络 培养生成模型 比较基于样本的两个概率分布 …

自学设计模式(简单工厂模式、工厂模式、抽象工厂模式)

使用工厂模式来生产某类对象&#xff08;代码简化且容易维护&#xff0c;类之间有血缘关系&#xff0c;可以通过工厂类进行生产&#xff09;&#xff1b; 简单工厂模式&#xff08;用于创建简单对象&#xff09; 对于简单工厂模式&#xff0c;需要的工厂类只有一个&#xff1…

CSS 属性值计算过程

目录 例子1&#xff0c;确定声明值2&#xff0c;层叠冲突2.1&#xff0c;比较源重要性2.2&#xff0c;比较优先级2.3&#xff0c;比较源次序 3&#xff0c;使用继承4&#xff0c;使用默认值其他 例子 我们来举例说明<h1> 标签最终的样式&#xff1a; <div><h1…

记录一个诡异的bug

将对接oa跳转到会议转写的项目oa/meetingtranslate项目发布到天宫&#xff0c;结果跳转到successPage后报错 这一看就是successPage接口名没对上啊&#xff0c;查了一下代码&#xff0c;没问题啊。 小心起见&#xff0c;我就把successPage的方法请求方式从Post改为Get和POST都…

Linux(基础篇二)

Linux基础篇 Linux基础篇二5. 系统管理5.1 Linux中的进程和服务5.3 systemctl5.4 运行级别CentOS 6CentOS 7 5.5 关机重启命令 Linux基础篇二 5. 系统管理 5.1 Linux中的进程和服务 计算机中&#xff0c;一个正在执行的程序或命令&#xff0c;被叫做“进程”(process) 启动之…

金融客户敏感信息的“精细化管控”新范式

目 录 01 客户信息保护三箭齐发&#xff0c;金融IT亟需把握四个原则‍ 02 制度制约阻碍信息保护的精细化管控 ‍‍‍‍‍‍‍ 03 敏感信息精细化管控范式的6个关键设计 04 分阶段实施&#xff0c;形成敏感信息管控的长效运营的机制 05 未来&#xff0c;新挑战与新机遇并存 …