[ZT]代码地震(作者:王咏刚 2004 年1 月)(7) 调用语句。修改时的任何一个遗漏都可能造成编译、链接乃至运行时的致命错误。 避免通信接口中参数表重复的方法是使用提炼后的整个数据结构作为参数来代替繁冗的参数表,例如,上面的接口方法例子可以改为: [WebMethod()] public int GetTransactionsCount(GTCParamStruct Param); 这样,当数据库结构的变化需要调整Web Service 接口时,只要改写GTCParamStruct 结构的定义就可以了。 用户界面中的代码重复 在“代码地震”的案例里,客户端程序用户界面组件中的代码重复占了代码重复总量的40%左右。其中,界面控件的重复相当普遍。例如,客户程序A 的数据录入界面、数据修改界面都需要罗列重复的控件,以录入或修改同样的数据内容,客户程序B 中的HTML 控件显示的也是完全相同的字段内容。当需求变更发生时,这些用户界面无一例外都需要修改。客户程序C 是呼叫中心使用的语音处理程序,其中的多个IVR 语音响应模块需要播报同样的字段内容,它们之间也存在相当多的代码重复。 一些程序员倾向于使用动态绑定和动态界面生成技术来解决用户界面的重复问题,即由动态生成程序根据数据库中的表结构或特定格式的界面描述语言③自动生成用户界面。不过,动态界面生成技术往往和具体的语言及开发环境相关,很难在不同语言间移植;动态生成的用户界面也总不如人工绘制的用户界面那样美观、精致。所以,大多数程序员目前仍然会选择自行绘制用户界面,并容忍界面代码重复的“权宜之计”。 界面编程中另一个值得注意的问题是数据合法性校验模块的代码重复问题。在数据录入、数据修改、数据查询等界面中,都需要对同样的字段进行合法性校验,这些校验代码显然都是一模一样的。如果在设计和开发时没有把校验代码提炼到公共组件中,那么,一旦业务需求改变了字段的构成规则,我们就会手忙脚乱、顾此失彼了。 并发控制组件中的代码重复 “银证通”项目组在需求变更时遇到的最大障碍是: 服务程序B 管理共享资源的代码和数据库结构密切相关, 其中也存在不少代码重复的问题。解决这些代码重复问题并不困难,真正困难的是,修改并发控制组件代码时的任何疏漏,都有可能酿成并发冲突、服务终止、数据不完整等难以预测和难以调试的“编程噩梦”。 因此,“银证通”项目组在系统升级的过程里,反复修改代码、反复测试并反复遭遇失败的经历提醒我们,对于涉及后台服务、并发控制等关键技术的软件系统,我们在此前反复强调的,在设计和开发过程里及早防范和减少代码重复的做法通常会具有更为重要的意义。 4 补充说明 有关代码重复的另一个定理是:在多人项目组内,如果没有好的配置管理工具和完善的版本控制手段,避免代码重复的提法就几乎等同于痴人说梦。 这是因为,解决程序之间、组件之间代码重复问题的主要手段是将重复的代码提炼出来,放到共享的公共库中。而在一个多人项目组内,开发和维护公共库代码的程序员和开发和维护其他组件的程序员,必须借助一个版本控制系统才能有效地协作,才不至于引发需求、版本或代码的混乱。当项目组拥有一套支持共享的版本控制机制、功能完善的版本控制软件时,我们甚至可以允许不同的程序员同时维护公共库的代码,这显然可以大幅提高工作效率, 并在最大程度上防止代码的重复(参见《凌波微步》一书中有关版本管理的章节④)。 5 总结一下 .. 重复代码意味着重复错误和重复风险;重复代码意味着增加维护成本和减少项目收益。 .. 在设计和开发过程中应及早防范和减少代码重复。 ① Fowler M 等著. 侯捷, 熊节译. 重构:改善既有代码的设计. 北京: 中国电力出版社, 2003 ② 郝刚. Duwamish7 大剖析之业务实体. CSDN 开发高手.