时隔一年,再议代码提取
首先说两件事情,我发现不同能力的人能掌控的代码行数并不一样,例如我,能掌控的一个函数长度也就在80行,而某些大牛能掌控的代码行数是一个函数150~300行(不过大牛要求这个函数相对清晰,也就是说这个函数可以做多件事情,但是函数内每段代码都只做一件事情,所以即使在函数内,代码也是一块一块的,否则大牛也一样觉得不爽),超过各自掌控的代码行数,都会觉得难受
所以在能力的掌控范围内,大牛并没有强烈的提取代码的需求,而我因为掌控能力不足所以即使在没有明显复用的情况下也有比较强烈的提取代码的需求,所以我重构的一些程序,往往重构后代码的长度并没有明显的减小(当然清晰程度是有所提升的)但是这样有一个问题,就是当只有一个调用点时,有的时候我并不知道提取出多少代码合适。
所以,这里可以为这篇文章定一个基调:提取代码作为一个重构的重要手段是是必要也是必须的,但是不能滥用,没有明显复用的情况下,要能容忍大函数,并理解消化它。这也是通往大牛之路的必经之路。
下面开始讲如何提取代码:一个函数应该只做一件事情,如果要提取,首先要识别代码里的函数,到底做了什么,做了一件事还是多件事,如果做了多件事,才能考虑是不是要提取。看下面的函数
void doSomething() { if(...) { // block1 } for(...) { // block2 } if(...) { // block3 } else { // block4 } // maybe more code }
也就是说当看到包含大段大段代码的多个控制结构出现在同一个函数内,这个函数就可能干了多件事情(当然,这样也并非就一定不好,看文章看头的内容),如果我们决定提取,那么怎么提取就是一个问题了,是否是每个控制结构就干了一件事情呢?也不一定,但假如一个控制结构就干了一件事情,那么是不是要连着控制结构一起提取呢?对于if/else结构来说比较好决定,因为这说明这件事情有不同的处理方式,要提取判断条件一起提显然还是合理的,但是单个if语句的情况就比较不好说了
if(...) { // do something }
这就回到了开头所说的,如果这个代码仅仅出于掌控能力不足提取出去的,那么应该是只有一个调用点,所以if本身是否需要提取实在是没有参考依据,说不上好坏,但如果提取之后的代码块有多个调用点,那么情况就比较明显了,除非每一个调用都必须进行判断和迭代,它们才需要被提取,所以牛头(我的一个大牛朋友)说,当只有一个调用点,看不出if需要迁出时,就保留在原地
那么如果if语句连着代码块一起提取,怎么表明代码并不是所有时候都会执行if语句内的代码呢?这个时候可以借助函数名来表达,例如
void removeNoteWhenMoreNote(...); // 即 do...When...的形式来命名
这是本篇文章最后一个环节:如何提高代码的掌控能力,这才是本质问题。我后来发现什么样的代码我不易掌控?第一是变量泛滥的代码,就是一个函数,不算函数参数至少得有10个局部变量,而且全部写在了函数块的顶部,读这种代码真是压力山大啊,再有内部纠缠不清的代码, so,代码如果是这样的应该就容易读了:
void doSomething() { // 做第一件事情 if(...) { //... } //做第二件事情 for(int i=0;i<SIZE;i++) { bool found = false; // .... } //做更多的事情 }
- 变量定义在第一次使用前而不是堆在函数开头
- 变量应该尽量局部化,出了作用范围,我应该能很放心的把他忘了
- 做相同事情的代码应该集中
posted on 2014-01-26 15:17 codestyle 阅读(2020) 评论(8) 编辑 收藏 举报