2010年(16)
分类: C/C++
2010-12-04 11:05:24
|
|
现在,至少看起来,这段代码已经比原来短了不少。当然这么做基于一个前提,就是这些字母编码的顺序确确实实是连续的。从理论上说,开始那段代码适用性更强。但在实际开发中,我们碰到字母不连续编码的概率趋近于0。
但 这段代码究竟是如何产生的呢?我开始研读上下文,原来这段代码是用当前ID产生下一个ID的,比如当前是N0000,下一个就是N0001。如果数字满 了,就改变字母,比如当前ID是R9999,下一个就是T0000。在这里,字母也就相当于一位数字,根据情况进行进位,所以有了这段代码。
代码上的注释告诉我,字母的序列只有从N到T,根据这个提示,我再次改写了这段代码:
|
这里统一处理了字母为T和default的情形,严格说来,这和原有代码并不完全等价。但这是了解了需求后做出的决定,换句话说,原有代码在这里的处理中存在漏洞。
修改这段代码,只是运用了非常简单的编程技巧。遗憾的是,即便如此简单的编程技巧,也不是所有开发人员都驾轻就熟的,很多人更习惯于“平铺 直叙”。 这种直白造就了代码中的许多鸿篇巨制。我听过不少“编程是体力活”的抱怨,不过,能把写程序干成体力活,也着实不值得同情。写程序,不动脑子,不体力才 怪。
无论何时何地,只要switch出现在眼前,请提高警惕,那里多半有坑。
很好的系列,成长的过程需要被总结和借鉴,可以整理成培训材料
有些情况看起来弱智,可现实都会存在。昨天看到一段代码,很典型的问题,列出来当素材吧:
if ( bussinessType == 0 ) {
if ( devId == 0 ) {
if ( linkId == 0 ) {
if ( year == 0 ) {
if ( month == 0 ) {
if ( day == 0 ) {
if ( hour == 0 ) {
if ( min == 0 ) {
return 0;
} else if ( min < 0 ) {
return -1;
}
return 1;
} else if ( hour < 0 ) {
return -1;
}
return 1;
} else if ( day < 0 ) {
return -1;
}
return 1;
} else if ( month < 0 ) {
return -1;
}
return 1;
} else if ( year < 0 ) {
return -1;
}
return 1;
} else if ( linkId < 0 ) {
return -1;
}
return 1;
} else if ( devId < 0 ) {
return -1;
}
return 1;
} else if ( bussinessType < 0 ) {
return -1;
}
return 1;
--------------------------------------------
减少第一次代码的“量”还是20%的功,真正的体力活是需求变更。将switch改成if我没看出任何灵活性。switch真正的危害是出现在需求变更的情况下。用多态将switch封装可以将变化点隔离在一处。但是作者确没有写.......
--------------------------------------------
用多态将switch封装是经典的手法,对于这个例子而言并不合适。针对这个需求,这么改是巧妙的。作者这篇文章的确没有说明“经典的switch陷阱”,但是体现了因地制宜的进行重构的精神,不如换个标题,“因地制宜的改造switch陷阱”。
--------------------------------------------
首先肯定,作者的最终优化代码更精简,不过可读性仍有改进的余地。
if (firstChar >= 'N' && firstChar <= 'S') {
nextFirstChar = firstChar + 1;
} else {
throw new IllegalArgument();
}
俺进一步改写为:
{
...
FirstCharShouldBeInLegalRange(firstChar);
nextFirstChar = GetNextFirstChar(firstChar);
...
}
private void FirstCharShouldBeInLegalRange(char value)
{
if (firstChar < 'N' || firstChar > 'S')
throw new IllegalArgument();
}
private char GetNextFirstChar(char value)
{
return (value + 1);
}
修改后的代码功能不变,但凭空多出来两个私有函数,
或许有人会说,这是多此一举(前半句就免了,实在不雅,哈哈)。
但可读性得到了提升,原有的 if else 被封装起来,取而代之的是两句平铺直叙的函数调用。
这种写法并非俺独创,在单元测试代码中遍地都是,但读起来一目了然,一气呵成,痛快之极!
读者根本不用考虑 switch-case 或是 if-else 等分支逻辑(都被封装起来了),
慢慢地俺喜欢上这种写法,把它应用到开发代码中,发现效果依然显著,代码可读性大大提高!
结论:switch-case 与 if-else 虽然可以相互转换、简化,
但在不可避免使用的情形下,将它们封装成语义明确的方法(函数)更有意义,
当然这种方法在 Martin Fowler 的 Refactoring 早就提到了,但想用好还需持续实践和反思!
是的,太正确了。变成IF会造成更多奇怪的代码,有时候反而不够switch好看,switch困扰了很多人,包括很多SSE。
如果每一个分支都有一段复杂逻辑,那么switch将变得非常难看。
幸
好在我所接触过的很多代码中,case里包裹的逻辑真得很少(只有一两行赋值而已),很多人认为逻辑简单,没有必要使用state,当出现Switch时
也就意味着状态实在太多了,抽取State会让人觉得烦琐,大多数人认为这样也工作得很好。坦率地讲,我不欢迎switch但也从来不拒绝。
以经验来看,大凡switch内分支都具备一些共性,一般地讲,所有的分支总是在处理同一批状态,于是我们才把这些状态封装成一个对象,而不同的Case则是不同的state,他们看起来会非常完美,但你仍旧得处理分支,你需要使用工厂来创建状态对象。
看了几篇,感觉写的很好,至少问题写的都很好,解决方法却是大家都有不同的方法,我更倾向于把对应的东西存入hashtable中,虽然这个例子中是有规律的,但并不是所有的情况都是有规律的,我一般会找到一种更加通用的方式!
把这一个系列都看了看,有两个我觉得容易给人误导的倾向,当然这个倾向已经快变成共识了。
简短的不重复的代码就一定强过啰嗦的代码。
声明式代码一定强于命令式代码。
作者不可能举过于复杂的例子,这是文章所限,不能强人所难,但问题是,在简单的场合,这两个原则的确是我认为有问题的,再多的原则,我以为都比不上“易读”这两个字。
有很多时候啰嗦的命令式代码恰恰是易读的,它甚至因为恰当的重复而达到这个目的。
过份在每个地方强调这样的写法,我怀疑会造成代码的阅读困难,交流困难,还可能带来一些不易察觉的bug。
简洁的声明式代码的强项更多的体现在架构设计,面对复杂变化时的场合。在例子中恰当的体现这一点,无疑对写作提出了更高的要求。
这些说法,在当前的潮流下可能显得反动了,哈哈。不如再提个更加反动的观点,有时候先写出易读,但是僵硬的代码,面临变化时再去重构,可能比一开始就考虑各种需求变化来得更好。
原因是:我们思考的层次本来就是如此递进的,不先把一些简单的线索先搞清楚,很难面对后面复杂的变化。
对代码风格,我觉得有一个是最高原则,无可置疑,这就是“易读”。
有一个是最高原则,无可置疑,这就是“易读”。
--请问,你怎么定义或理解“易读”?我想“易读”是指容易读懂吧,这跟个人素质有关
的。例如,台湾同学对同一篇技术文章,从右至左的看繁体字,比大陆同学从左至右看简体字,算是“易读”吧;常春藤名校的、以UNIX和LISP背景的同
学,比国内的WINDOWS和C背景的同学看同一段程序,“易读”的感觉不一样吧?
简短的不重复的代码就一定强过啰嗦的代码。
声明式代码一定强于命令式代码。
--很显然,以上的命题是正确的,除非读者一直以来,是以WINDOWS+C/JAVA等为背景的程序员,没有在UNIX/LINUX下的函数式编程的经历或兴趣。