重构之重与敏捷之轻---身份证号重构回顾

寻找重构的案例

重构的案例不好选取,我们自己实际的项目当然有不少重构的情况,却不适合作为文章阐述出来。并不是因为保密的考量,而是业务本身的内容太复杂。当然,这个复杂是相对的,对短短一篇文章来说,那怕是最小一部分的业务逻辑拿出来,也显容量不够。而且,过多的阐述业务逻辑,导致喧宾夺主,掩盖了重构的注意力。所以,要选取一个大家都熟悉的东西,身份证号恰恰中了我第一个要求。

即然是重构,必然要有个原始的版本,而且,要完成了实际的功能。开源中国的代码分享也刚好为我提供了一个资源库。代码分享这个平台,真是太理想了,代码规模不大不小,每一个分享又有一个明确的目标功能。
看起来,确实是个金矿。可是随着深入的搜索查看,能进入我目标范围的却是寥寥无几。大部分代码只是粘合剂,把外部库的调用综合起来,完成一个任务而已。还有些代码,方法体太大,而代码却是简单重复,似乎重构的必要,却未能展现出重构该有的不同侧面,而且也有前面所提到的毛病,是粘合剂代码块。
(本文版权属于© 2012 - 2013 予沁安

而当我看到身份证这三个字,才眼睛一亮,看似简单的身份征号包括了太多内容:

  1. 它是一个典型业务域的Value Object(不要与C#语法概念的Value Object混淆),我们自己的系统中,刚好也有做一个身份证号值对象的想法。之前我们已经作一个月份对象(YearMonth),非常好用,但是,因为功能多代码量大,不适合作短篇。
  2. 它没有对外部库的依赖。不是说,我只能对这种独立的代码重构,而是限于篇幅和重点突出的考虑。
  3. 它的业务逻辑很大众化,谁没有身份证呢?
  4. 分享代码已经把足够的业务逻辑包含进起,我所做的就是保持这个不变全力集中在重构上。再完美不过的重构案例了。

敏捷的方式重构和作文

让子弹飞

定下了目标,当开始做的时候,满脑子都是想法。原代码是Java,要改成C#; getXX可以用属性方式,更为简捷;既然是Value Object,似乎用Struct比用Class更合适;作为Value,它应该是个不变类(Immutable),因此不应该动态解析,在构造器中解析,更为恰当; Validation的实现也不太合适……
是的,像我这样资深的敏捷实践者,在第一时间仍有这种冲动,太多想法马上实现的冲动。这其实是人的一种天性,没关系,我就让思维的子弹先飞一会儿。
子弹乱飞之后的寂静,我想到的第一件事情就是补测试。说到测试,我用MSpec曾屡屡想为MSpec做个入门介绍,却一直未能成文,而直接使用MSpec的博文我却已经写了不少, 大家就在实际用例中学习Machine.Specification吧。
说是补测试,其实从Java到C#的编泽错误修改已经开始了不少改动。测试这一块是原代码完全缺失,补上这个,可以说是重构的一部分。

迭代一:

准备工作做了不少,到最后,真正测试只写了一个GetAddress()。而这成为了我的第一天。第一个测试成为我第一天的目标,围绕这个目标,我放弃一切与此无关的工作,特别是之前子弹乱飞的内容。

迭代二:

第二天,要做的事情一下子非常明确,按部就班的把其它的功能测试一一补全:生日,性别等等。几乎不太用脑子,有时候,事情就是这样,有波涛汹涌,有风平浪静,唯有测试是船舵。
从测试的条数来看,第二天进展很快。写出来的内容也显丰富。

迭代三:

第三天的进展就顺利多了。把各个功能测试一一补全通过,如生日,性别和验证。其中有个小插曲,验证逻辑提炼成独立的类。总结起来没有什么好说的,重构本身却有看头。

迭代四:

到了第四天,才真正开始做我想要做的事情,整理逻辑,清理代码。再加上代码覆盖工具DotCover的帮助,VS的复杂度分析,从另一个侧面对重构效果加以验证。其实应该在重构之前也分析一下代码,两相比较一下,效果会好很多。

从此,xx和xx过上了幸福的生活......

我的博文算是结束了,可是作为代码的改进,其实并没有结束,甚至我最开始的想法,有一部分都还没有完成,如struct。我还丢掉了一部分业务,如15位号码转换18位。这是作为重构的展示已经足够。最重要的是,我已经创立了一个健壮的<测试-实现>对,这个基础上你可以继续完善。

代码本身的技术含量并不多,但重构的流程,任务细分,小步前进的理念,却表达的非常充分。作为体验,如果你能按照我走过的流程,再踏踏实实走一遍,会有完全不同的感觉。敏捷是实践,重构是实践,唯有实践才能体验实践。

(本文版权属于© 2012 - 2013 予沁安 | 转载请注明作者和出处

posted @ 2013-04-22 11:35 予沁安 阅读(...) 评论(...) 编辑 收藏