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…

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

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

用MFC打开外部程序

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

牛客练习赛 114

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

【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…

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

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

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;新挑战与新机遇并存 …

【无标题】jenkins消息模板(飞书)

这里写目录标题 Jenkins 安装的插件 发送消息到飞书预览 1 &#xff08;单Job&#xff09;预览 2 &#xff08;多Job&#xff0c;概览&#xff09; Jenkins 安装的插件 插件名称作用Rebuilder Rebuilder。 官方地址&#xff1a;https://plugins.jenkins.io/rebuild 安装方式&a…

vue组装模板(侧边栏+顶部+主体)--项目阶段4

目录 一、前言介绍 二、结构解析 三、页面拆分 &#xff08;一&#xff09;页面拆分 1.侧边栏页面&#xff08;固定&#xff09;--Aside.vue 2.顶部页面&#xff08;固定&#xff09;--Header.vue 3.主体页面&#xff08;不固定的&#xff09;--示例用UserView…

【位运算进阶之----左移(<<)】

今天我们来谈谈左移这件事。 ❤️简单来说&#xff0c;对一个数左移就是在其的二进制表达末尾添0。左移一位添一个0&#xff0c;结果就是乘以2&#xff1b;左移两位添两个0&#xff0c;结果就乘以2 ^ 2&#xff1b;左移n位添n个0&#xff0c;结果就是乘以2 ^ n&#xff0c;小心…

小白到运维工程师自学之路 第七十九集 (基于Jenkins自动打包并部署Tomcat环境)2

紧接上文 4、新建Maven项目 clean package -Dmaven.test.skiptrue 用于构建项目并跳过执行测试 拉到最后选择构建后操作 SSH server webExec command scp 192.168.77.18:/root/.jenkins/workspace/probe/psi-probe-web/target/probe.war /usr/local/tomcat/webapps/ /usr/loca…

电商设计之分类模块设计

1、现在店铺流行这些简单的风格 2、 3、夏季新品 4、妖精的口袋----店铺展示 5、小狗电器-----优秀分类案例 6、客服中心 7、有线手持款---这里全都是有线首饰款&#xff0c;方便找到东西 8、裂帛 8.1裂帛分类模块 8.2 我点击了T恤 8.3 买雪纺衫&#xff0c;这里面全都是雪纺 …

mybatis与spring集成与spring aop集成pagehelper插件

Mybatis与Spring的集成 Mybatis是一款轻量级的ORM框架&#xff0c;而Spring是一个全栈式的框架&#xff0c;二者的结合可以让我们更加高效地进行数据持久化操作。 Mybatis与Spring的集成主要有两种方式&#xff1a;使用Spring的Mybatis支持和使用Mybatis的Spring支持。 使用…

python网络爬虫指南二:多线程网络爬虫、动态内容爬取(待续)

文章目录 一、多线程网络爬虫1.1 线程的基础内容、GIL1.2 创建线程的两种方式1.3 threading.Thread类1.4 线程常用方法和锁机制1.5 生产者-消费者模式1.5.1 生产者-消费者模式简介1.5.2 Condition 类协调线程 1.6 线程中的安全队列1.6 多线程爬取王者荣耀壁纸1.6.1 网页分析1.6…