http://blog.2baxb.me/archives/1499
小规模的重构应该跟敲代码一样属于日常开发的一部分。
随时进行模块内部的重构
一次只做一个较模块级别的的重构
工程级别的重构不能和任何其他任务并行
2.1 意义不明
2.2 不说人话
2.3 不恰当的组织
2.4.假设和缺少抽象
2.6 够用的代码
3、重构不是万能药
从技术上来说,重构复杂代码时,要做三件事:理解旧代码、分解旧代码、构建新代码。而待重构的旧代码往往难以理解;模块之间过度耦合导致牵一发而动全身,不易控制影响范围;旧代码不易测试导致无法保证新代码的正确性。
这里还有一个核心问题,重构的复杂度跟代码的复杂度不是线性相关的。比如有 1000 行烂代码,重构要花 1 个小时,那么 5000 行烂代码的重构可能要花 2、3 天。要对一个失去控制的工程做重构,往往还不如重写更有效率。
4、写好代码很难
与写出烂代码不同的是,想写出好代码有很多前提:
理解要开发的功能需求。
了解程序的运行原理。
做出合理的抽象。
组织复杂的逻辑。
对自己开发效率的正确估算。
持续不断的练习。
http://blog.2baxb.me/archives/1378
可维护的代码
避免重复
模块划分
Dependencies Analysis功能,学会这些工具的使用对于评价代码质量会有很大的帮助。
《Software Architecture Patterns-Understanding Common Architecture Patterns and When to Use Them》
http://blog.2baxb.me/archives/878
https://blog.codinghorror.com/flattening-arrow-code/
http://coolshell.cn/articles/17757.html
小规模的重构应该跟敲代码一样属于日常开发的一部分。
随时进行模块内部的重构
一次只做一个较模块级别的的重构
工程级别的重构不能和任何其他任务并行
因此提前发现代码变烂的趋势很重要,这类工作可以依赖类似于checkstyle,findbug之类的静态检查工具,及时发现代码质量下滑的趋势,例如:
- 每天都在产生大量的新代码
- 测试覆盖率下降
- 静态检查的问题增多
有了代码仓库之后,就可以把这种工具与仓库的触发机制结合起来,每次提交的时候做覆盖率、静态代码检查等工作,jenkins+sonarqube或者类似的工具就可以完成基本的流程:伴随着代码提交进行各种静态检查、运行各种测试、生成报告并供人参考。
- 烂代码无法避免
- 烂代码无法接受
- 烂代码可以改进
- 好的代码能让工作更开心一些
如何让大多数人认同关于代码质量的观点实际上是有一些难度的,大部分技术人员对代码质量的观点是既不赞成、也不反对的中立态度,而代码质量就像是熵值一样,放着不管总是会像更加混乱的方向演进,并且写烂代码的成本实在是太低了,以至于一个实习生花上一个礼拜就可以毁了你花了半年精心设计的工程。
所以在提高代码质量时,务必想办法拉上团队里的其他人一起。虽然“引导团队提高代码质量”这件事情一开始会很辛苦,但是一旦有了一些支持者,并且有了可以参考的模板之后,剩下的工作就简单多了。
关于烂代码的那些事 - 为什么每个团队存在大量烂代码“‘更好’,其实不是一个目的地,而是一个方向…在当前的位置和将来的目标之间,可能有很多相当不错的地方。你只需关注离开现在的位置,而不要关心去向何方。”
2.1 意义不明
2.2 不说人话
2.3 不恰当的组织
2.4.假设和缺少抽象
2.6 够用的代码
3、重构不是万能药
从技术上来说,重构复杂代码时,要做三件事:理解旧代码、分解旧代码、构建新代码。而待重构的旧代码往往难以理解;模块之间过度耦合导致牵一发而动全身,不易控制影响范围;旧代码不易测试导致无法保证新代码的正确性。
这里还有一个核心问题,重构的复杂度跟代码的复杂度不是线性相关的。比如有 1000 行烂代码,重构要花 1 个小时,那么 5000 行烂代码的重构可能要花 2、3 天。要对一个失去控制的工程做重构,往往还不如重写更有效率。
4、写好代码很难
与写出烂代码不同的是,想写出好代码有很多前提:
理解要开发的功能需求。
了解程序的运行原理。
做出合理的抽象。
组织复杂的逻辑。
对自己开发效率的正确估算。
持续不断的练习。
http://blog.2baxb.me/archives/1378
- 日志是否足够,所有异常、外部调用都需要有日志,而一条调用链路上的入口、出口和路径关键点上也需要有日志。
- 日志的表达是否清晰,包括是否能读懂,风格是否统一等。这个的评价标准跟代码的可读性一样,不重复了。
- 日志是否包含了足够的信息,这里包括了调用的上下文、外部的返回值,用于查询的关键字等,便于分析信息。
可维护的代码
避免重复
模块划分
Dependencies Analysis功能,学会这些工具的使用对于评价代码质量会有很大的帮助。
http://blog.2baxb.me/archives/878
高耦合
假如在面试的时候问求职者:面向对象设计需要遵循什么原则呀?大概没有人会答错:高内聚低耦合。但工作中能做到这一点的人却少之又少,于是便出现了一些几万行代码的大工程;几十个方法的Service类;几百行的函数,等等。
后来便出现了这种事情:想要依赖某个工程里一个简单的方法,加上依赖之后发现打完包的工程里莫名其妙多了几十个jar包和满屏幕的依赖冲突;改一个简单逻辑却无从下手,只能一遍又一遍的研究那段成百上千行的函数……
后来便出现了这种事情:想要依赖某个工程里一个简单的方法,加上依赖之后发现打完包的工程里莫名其妙多了几十个jar包和满屏幕的依赖冲突;改一个简单逻辑却无从下手,只能一遍又一遍的研究那段成百上千行的函数……
拷代码
编码
magic number、疯狂的if嵌套、莫名其妙的变量名、梦呓一般的注释,编码大概是烂代码界最能玩出花样的地方。
更烂的代码
一旦某个工程里有了烂代码,那么就会像招苍蝇一样招来更烂的代码,假如一个团队里都是这种工程,那么这个团队代码质量下降的速度之快、幅度之大都会令人咋舌。
常规重构只应该存在于某一次的开发过程中,把重构当做后续方案的人要么没做过重构,要么只是拿以后重构当做借口- 前段时间,有人看到我在纠结一个类应该如何设计,便问我:代码质量这玩意不算KPI,这个破工程说不定做完了都没人用,你在那里纠结个啥劲?我回答他,如果平时都在写烂代码,真正需要需要的时候还能快速的写出好代码吗?
- 烂代码和烂设计从来都是形影不离的好基友,从UML开始review会省掉很多写无用代码的时间。
- 好的框架是好代码的一半。
- 对新人来说代码质量远重要于开发效率,不要让新人做“下周二上线”之类的工作。
- 代码质量的关注点更多的应该是“找到更简单的发现/改善代码质量问题的方法”,单纯的强调“代码质量的意义”没有意义。
https://blog.codinghorror.com/flattening-arrow-code/
- Replace conditions with guard clauses. This code..
if (SomeNecessaryCondition) { // function body code }
.. works better as a guard clause:if (!SomeNecessaryCondition) { throw new RequiredConditionMissingException; } // function body code
- Decompose conditional blocks into seperate functions. In the above example, we're in a do..while loop which could be decomposed:
do { ValidateRowAttribute(drc[rowIdx]); rowIdx++; } while(rowIdx < rowCount && GetIdAsInt32(drc[rowIdx]) == Id);
- Convert negative checks into positive checks. As a broad rule, I prefer to put the positive comparison first and let the negative comparison fall out naturally into the else clause. I think this reads a lot better and, more importantly, avoids the "I ain't not never doing that" syndrome:
if (Attributes[attrVal.AttributeClassId] is ArrayList) { // do stuff } else { // do stuff }
- Always opportunistically return as soon as possible from the function. Once your work is done, get the heck out of there! This isn't always possible – you might have resources you need to clean up. But whatever you do, you have to abandon the ill-conceived idea that there should only be on
double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetired) return retiredAmount(); return normalPayAmount(); };
http://coolshell.cn/articles/17757.html
for
(....) {
do_before_cond1();
if
( !cond1 ) {
do_after_cond1();
continue
}
do_after_cond1();
do_before_cond2();
if
( !cond2 ) {
do_after_cond2();
continue
;
}
do_after_cond2();
do_before_cond3();
if
( !cond3 ) {
do_after_cond3();
continue
;
}
do_after_cond3();
do_something();
}
但是,如果它们之前没有依赖关系的话,根据 DRY 原则,我们就可以只保留一份,那么直接掉到 if 条件前就好了,如下所示:
for (....) { do_before_cond1(); do_after_cond1(); if ( !cond1 ) continue ; do_before_cond2(); do_after_cond2(); if ( !cond2 ) continue ; do_before_cond3(); do_after_cond3(); if ( !cond3 ) continue ; do_something(); }
|
1)使用 Guard Clauses 。 尽可能的让出错的先返回, 这样后面就会得到干净的代码。
2)把条件中的语句块抽取成函数。 有人说:“如果代码不共享,就不要抽取成函数!”,持有这个观点的人太死读书了。函数是代码的封装或是抽象,并不一定用来作代码共享使用,函数用于屏蔽细节,让其它代码耦合于接口而不是细节实现,这会让我们的代码更为简单,简单的东西都能让人易读也易维护,写出让人易读易维护的代码才是重构代码的初衷!
3)对于出错处理,使用try-catch异常处理和RAII机制。返回码的出错处理有很多问题,比如:A) 返回码可以被忽略,B) 出错处理的代码和正常处理的代码混在一起,C) 造成函数接口污染,比如像atoi()这种错误码和返回值共用的糟糕的函数。
4)对于多个状态的判断和组合,如果复杂了,可以使用“组合状态表”,或是状态机加Observer的状态订阅的设计模式。这样的代码即解了耦,也干净简单,同样有很强的扩展性。
5) 重构“箭头型”代码其实是在帮你重新梳理所有的代码和逻辑,这个过程非常值得为之付出。重新整思路去想尽一切办法简化代码的过程本身就可以让人成长。