fizzbuzz牛津英语树 fizzbuzz( 五 )


迭代2前面的做完,新的需求来了:
你是一名体育老师,在某次距离下课还有五分钟时,你决定搞一个游戏 。此时有200名学生在上课 。游戏的规则是:
让所有学生拍成一队,然后按顺序报数 。学生报数时,如果所报数字是3的倍数,那么不能说该数字,而要说Fizz;如果所报数字是5的倍数,那么要说Buzz;如果所报数字是第7的倍数,那么要说Whizz 。学生报数时,如果所报数字同时是两个特殊数的倍数情况下,也要特殊处理,比如3和5的倍数,那么不能说该数字,而是牛津要说FizzBuzz,以此类推 。如果同时是三个特殊数的倍数,那么要说FizzBuzzWhizz 。迭代2的需求改动不多,这个需求对我们的业务域造成的变化是加入了一个新的特殊数字7 。如果我们还是按照迭代1的方式去实现,我们写出来的代码可能很可能游戏如下所示:

fizzbuzz牛津英语树 fizzbuzz

文章插图
代码有什么问题吗?它最大的问题叫做圈复杂度太高 。
圈复杂度圈复杂度的计算中文方式是这样的,每当你看到一个for或者if或者while,总之,每看到一个判断分支或者是一个循环分支,圈复杂度就加1 。每当看到continue和break也加1,return也加1 。加完了就是这一部分代码的圈复杂度了 。
圈复杂度高了会怎么样呢?圈复杂度高低与bug率高低有强相关性 。在各种测试指标当中,很少有像圈复杂度这样与千行代码bug率强相关的指标,相关度高达百分之九十几 。也就是每千行代码,圈复杂度越高BUG率就越高 。虽然不是因果性,但是对于一个工程学科来说,相关性也有足够多的指导意义了,所以当我们看到圈复杂度比较高的代码的时候,就要考虑重构掉 。
重构与十六字箴言这回我们就真的需要重构了,具体重构要怎么做呢?难道是把这块删了重写吗?那就有点糙了 。但精细的讲,重构是有60多种手法的,也没谁都能记住啊 。不过好在总的来说手法的模式是很相近的python,我们有个同事总结了四句话,我们戏称为“16字箴言”,内容如下:
旧的不变新的创建一步切换旧的再见什么意思呢?首先不要着急改掉旧的代码,先让旧的保持不变,不过因为intelliJ这种利器的存在,使得抽取函数本身不再是一件危险的事(起码在Java里是这样),所以我们通常会先把要重构的旧的代码抽个函数,让重构的目标显性化 。做这一步的时候,你会发现可能已经要改变代码结构了,起码要改造成我前面所说消除过程依赖,让代码之间只有数据依赖,这样才好提取嘛 。提取之后写个新实现,然后在调用点调用新实现,旧的调用点先注释掉,测试通过了,在把旧的调用点代码删掉,打扫战场把旧的实现也删掉 。
具体到这个题呢,我的做法会是如下:
先消除过程依赖 。
然后抽取函数,把要读音重构的代码块先通过函数封起来,划定重构的边界,把输入输出浮现音标出来 。
fizzbuzz牛津英语树 fizzbuzz

文章插图
接着写一个新函数 。
fizzbuzz牛津英语树 fizzbuzz

文章插图
然后把函数调用点换掉 。
fizzbuzz牛津英语树 fizzbuzz

文章插图
然后把旧的函数删掉,打扫现场,该改名改名,该去注释去注释 。
fizzbuzz牛津英语树 fizzbuzz

文章插图
上面每一步结束的时候都要保证测试是通过的 。软件工程当中有一句很重要的理念:一个问题发现的越晚修正它的成本就越高 。本着这个思想,我们重构的时候也要是这个样子,每做一次修改都要看一看有没有问题,如果有问题就立刻修正 。如此小步前进,才是我们所谓的重构 。

相关经验推荐