《重构,改善代码的既有设计》02-重构的原则

02重构的原则

2.1何为重构

重构的定义

使用一系列的手法,对软件内部结构的一种调整,目的是:
	在不改变软件可观察行为的前提下,
	提高可理解性,
	减低修改成本(时间成本、人力成本、质量成本);

重构的关键

运用大量微小且保持软件行为的步骤,一步步达成大规模的修改;
重构过程中,每一步改动后软件保持可用状态;

重构和优化的对比

***相同点***
	都需要修改代码;
	都不会改变程序的整体功能;
***不同点***
	重构的目的
		重构是为了让软件更容易理解、更容易修改;
		可能使得程序运行更快,也可能使程序运行更慢;
	优化的目的
		只关心让程序运行更快;
		最终的代码可能更难以理解和维护;

2.2两顶帽子

两顶帽子 from Kent.Beck

使用重构技术开发软件时,我把自己的行为分配给2种截然不同的行为:
	添加新功能
	重构

添加新功能

	添加新功能时,我不应该修改既有代码,只管添加新功能;
	通过添加测试并让测试运行正常,我可以衡量自己的工作进度;

重构

	重构时,我就不能添加功能,只管调整代码的结构;
	此时我不应该添加任何测试;
	除非发现之前有遗漏的功能;
	只在绝对必要时修改测试(处理接口变化);

2.3为何重构

重构改进软件的设计

	程序的内部设计或者架构会逐渐腐败变质;
	人们只为短期目的修改代码时,经常没有完全理解架构,于是代码逐渐失去了自己的结构;
	代码结构的流失有累积效应,越难理解,越难保护,腐败的越快;
	经常重构,有助于代码维持应有的形态;
	
	改进设计的一个重要方向就是,
		消除重复代码;
		**确定所有的事物和行为在代码里只表述一次------这是优秀设计的根本**

重构使软件更容易理解;

	对于代码,除了计算机之外,还有其他读者;
	后续的读者,要读懂或修改代码,对比计算机,这些读者才是更重要的;
	很多时候,这位后续的读者,便是我们自己;
	
	重构,帮助我们更清晰的表达,程序逻辑;

重构帮助找到bug

	对于代码的理解,可以帮助我们找到bug;
	而对代码进行重构,
		就可以深入理解代码的所作所为,
		并立即把新的理解反应在代码中;
		搞清楚代码结构的同时,可以验证自己的一些假设;
		于是,bug无处可藏;
		
	“**我不是一个特别好的程序员,我只是一个有着特别好的习惯的还不错的程序员**”---from Kent.Beck

重构提高编程速度

	重构可以提高质量
		改善设计、提升可读性、减少bug;
	在短期内可能花费一些时间在重构上,
		随着时间的推移和功能的负责,重构可以大大的提高开发速度;
	
	**两种团队**
		需要添加新功能时,内部质量良好的软件,可以很容易的找到在哪儿修改、如何修改;
		良好的模块划分,使我只需要理解代码库的一小部分,就可以做出修改;
		代码清晰,引入bug的概率就会变小;
		即时引入了bug,调试定位也会容易的多;
		理想情况下,质量高的代码,会逐渐演化成一个平台
			在其上,可以很容易的构造与其领域相关的新功能;
		这种现象称之为“**设计耐久性假说**”,
			通过投入精力概设内部设计,增加软件的耐久性,从而可以更长时间的保持开发的快速;

2.4何时重构

三次法则

第一次做某件事,只管去做;
第二次做类似的事,会产生反感,但是无论如何,还是可以去做;
第三次,再做类似的事,你就应该重构;
即为:
	**事不过三,三则重构**;

重构的6种方式

方式1,预备性重构,让添加新功能,更容易;

重构的最佳时机:
	就在添加新功能前;
对代码结构做一些调整,添加新功能就会很容易;
如果不做重构,
	就需要复制结构类似代码,违背只表述一次逻辑;
	将来需要修改,就需要修改多处;

方式2,帮助理解的重构,是代码更易懂;

修改一两个变量名,让他们更清晰的表达意图;
将一个常函数,拆分成几个小函数,结构明了;

“**初步的重构,就像扫去窗上的尘埃,使我们得以看到窗外的风景**”---from Ralph Johnson

重构引领我们获得更高层次的理解;
	缺少了这些细微的理解,就无法看到隐藏在混乱背后的危机;

方式3,捡垃圾式重构

确认代码中的垃圾:
	函数迂回复杂、代码重复;

如果垃圾很容易重构,那就重构他;

如果时间和精力不允许,那就把它记下来,完成当下的任务后再去重构他;

**让营地比你来的时候更整洁**;

重构的妙处:
	每个小步骤,都不会破坏代码;
	即便一块垃圾在好几个月之后才终于处理干净,
	即便每次清理垃圾并不完整;
	代码也不会被破坏;	

方式4,有计划的重构和见机行事的重构

肮脏的代码需要重构;
	但漂亮的代码,也需要很多重构;

“**每次要修改时,首先令修改很容易(有时候,会很难),然后再进行这次容易的修改**”---from Kent Beck

有计划的重构
	如果他对过去忽视了重构,那么需要花一些时间来优化代码库;
		对于很多公司来说,这是一种常态;
	在重构上花一个星期的时间,会在未来几个月发挥价值;
见机行事的重构
	大部分的重构,应该是不起眼的,见机行事的行为;
	开发过程中遇到问题,重构使得工作变得简单或者代码清晰;
		
将重构与添加新功能在版本控制中,分开添加;
	可以各自独立的审阅和批准这些提交;
	这个不是毋庸置疑的原则,当你感觉有价值时,就这样做;

方式5,长期重构;

大型重构需要花费较长时间;
	比如替换一个正在使用的库;
	比如把代码抽取出来给另外模块使用;
	比如处理一大堆的混乱关系;
编码让一个团队专门做重构
	有效策略:
		让整个团队达成共识;
		未来的一段时间,逐步解决问题;
	策略优点:
		重构不会破坏代码;
		每次小改动,整个系统正常工作;
		
替换一个正在使用的库
	先引入一层新的抽象;
	使其兼容新旧两个库的接口;
	一旦调用方已经完全改为使用这层抽象;
	替换下边的库将会容易的多;
	**Branch By Abstraction**

方式6,代码审核时重构

Dont know why

如何对经理说

坚持做正确的事;

何时不应该重构

如果看见一块凌乱的代码,
	如果不需要修改它,那么久不需要重构;
	只有当需要理解其工作原理时,对其重构才有价值;

如果重写比重构还容易,那就别重构了;

2.5重构的挑战

挑战1-延缓新功能开发

重构的唯一目的
	就是让我们开发的更快;
	用更少的工作量,创造更大的价值;
特殊情况
	一个大规模的重构很有必要进行;
	马上要添加的功能非常小;
	先把功能添加上,再完成这次大规模的重构;

如果一个问题我已经见过;
	我更倾向于重构它;
有时一块丑陋的代码,我得先看见它几次;
	我才有劲头去重构它;
如果一块代码,我很少触碰或者它不经常带来麻烦,
	我就不去重构它;
如果我还没想清楚如何优化代码,
	我倾向于延迟重构;
	或者先做些实验,测试下能否有所改进;

挑战2-代码所有权

重构的影响范围
	重构会影响模块内部;
	重构会影响模块与系统其它部分的关系;
		修改函数名;
		函数的调用,可能由另一支团队的代码;
		函数的调用,可能由客户的代码调用;
代码所有权的边界,会妨碍重构;
	函数改名,需要保持原来的函数声明;
	使其把调用传递给新的函数;
	这会让接口变得复杂,
	这就是为了避免破坏使用者的系统不得不付出的代价;
	我们可以把旧的接口标记为“不推荐使用”
		让旧的接口逐步退休;
		有些时候,旧的接口必须一直保留下去;

挑战3-主干-分支开发方式

常见的主干-分支开发方式
	在分支上开发完整的功能;
	直到功能可以发布到生产环境,才把代码合并会主干;
	优点:
		能够保持主干不受尚未完成的代码影响;
		能够保留清晰的功能添加版本记录;
		在某个功能出问题时,可以很容易的撤销修改;
	缺点:
		在分支上开发的越久,合并会主干的难度越大;
		合并难度,随着时间,指数上升;
解决方法
	持续集成(Continuous Integration);
	每个团队开发成员,每天至少向主干集成一次;
	避免了任何分支彼此差异太大,从而大大降低了合并的难度;

挑战4-测试

重构的重要特征
	不会改变程序的可观察行为;
	我们总是会犯错误,每个重构步骤都是微小的改变;
	造成bug时,我们只需要检查最后一步的小修改;
	如果找不到原因,我们可以回滚到版本控制中的最后一个可用版本;
快速发现错误
	要做到这点,我们要有一整套的完备测试套件;
	且,执行速度要快,否则没人愿意频繁的运行测试;
	绝大多数情况下,
		先要重构,先要有可以自测试的代码;
自测试代码
	自测试的代码,要求很高;
	团队在此投入时间和经理,绝对是物超所值的;
	自测试代码,使得重构称为可能,而且添加新功能更加安全;
	自测试代码,回答了“重构风险太大,可能引入bug”的问题;
		如果没有自测试代码,这种担忧是完全合理的;
自动化重构
	C语言上没什么实现;

挑战5-遗留代码

重构可以让我们很好的理解遗留系统;
“没有测试就加上测试”
	很难完整执行;
	没有简单的解决办法;

挑战6-数据库重构

解决数据库重构经常出问题的方法:
	借助数据迁移脚本;
	将数据库结构的修改和代码结合;
	使得大规模的、涉及数据库的修改可以比较容易的开展;
数据库重构的关键
	小步修改;
	每次修改完整;
	分布到多次生产发布来完成;
	即便某次修改造成了问题,比较容易回滚;

2.6重构、架构、YAGNI

重构与架构

重构极大的改变了人们考虑软件架构的方式;
早起架构思维:
	在任何人写代码之前,必须先要完成软件的设计和结构;
	但是,只有真正使用了软件,看到软件对工作的影响,人们才会明白自己到底需要什么;
	
重构技术,使得即便运行了很多年的程序,也可以大规模的修改其架构;

重构对架构的最大影响:
	通过重构,
	能够得到一个设计良好的代码库;
	使其能够 优雅 应对不断变化的需求;

YAGNI

You Aren't Going to Nead It,你不会需要它;
个人理解:
	代码功能,不超前、不滞后、重构到最需要的状态;

2.7重构与软件开发过程

重构是否有效

与团队采用的其他软件开发实践紧密相关;

重构的基石

重构的第一块基石
	自测试代码;
重构的第二块基石
	每个团队的成员都要掌握重构技能;
	能够在需要时展开重构,而不会影响他人的工作;
	这就是我鼓励CI(持续集成)的原因

协同效应

自测试代码;
持续集成;
重构;
YAGNI;
重构是YAGNI的基础;
YAGNI使得重构易于开展;

2.8重构与性能

编写快速软件的秘密

除了对性能有严苛要求的实时系统;
先写出可调优的代码;
然后调优以求获得足够的速度;

三种编写快速软件的方法

方法1、最严苛的时间预算法

每个组件分配一定的时间和空间,严格遵循;

方法2、持续关注法;

程序开发,要求任何程序员,在任何时间、做任何事,都要设法保持系统高效能;
通常没有效果;

**经验教训**
		哪怕你完全理解系统;
		也请实际度量它的性能;
		臆测会让你学到一些东西,但是臆测有时候不一定是对的;
	
**关于性能**
		如果你对大多数程序进行分析,就会发现,
		大半时间都耗费在一小半的代码上;
		如果一视同仁的优化所有代码,
		九成的优化是白费劲的;
		因为被优化的代码很少被执行;

方法3、性能提升法;

	利用关于性能的”九成“的统计数据,优化性能;
	编写代码时,只注重构造良好的程序;
	不对性能投以特别的关注;
	直至性能调优阶段,再遵循特定的流程来调优程序性能;
	
	利用一个度量工具监控程序的运行;
	分析出程序中哪些地方大量的消耗时间和空间;
	
	**重构可以帮助优化性能;**
		构造良好的代码,让我们有比较充裕的时间进行先调整;
			能够更快的添加新功能,有更多的时间用于性能调优;
		构造良好的代码,在进行性能分析时有较细的粒度;
			度量工具可以把我们带入范围较小的代码段;
			从而性能调优也比较简单;

2.9重构的起源

重构,没有真正的起源;

2.10自动化重构

开发工具的支持;
Java	
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值