[ZT]代码地震(作者:王咏刚 2004 年1 月)(4) 管理均摊和设备折旧成本、差旅和办公费用、工资福利等)。比较难办的是分子V。通常的想法是直接用项目的销售额来反映项目组为公司创造的效益。但我们必须考虑公司的一些特殊项目。比如,为了打入某个新的行业,公司可以牺牲利润与客户签单。这样一来,销售额就反映不出该项目对公司的价值了。再比如,一些研发性项目在短时间内无法带来任何实际的销售额,其效益该如何计算呢?我个人的意见是,项目的实际效益可以比照正常项目的销售额来计算,但最终的决定权在公司的项目监管部门。比如, 一个在正常竞争情况下可以获得100 万元销售额的项目, 因为在特殊情况下的让利,实际销售额是75 万元,那么, 项目监管部门在计算项目组的贡献时,应当以100 万元为计算依据(确定研发项目实际效益的方法相对复杂一些, 这里就不再展开了)。 拿本文案例中提到的“银证通”项目来说,项目组按时完成了开发工作,假如保内维护工作也相对正常,那么, 项目组的“贡献率”自然不会很低。对于案例中简单的需求变更,无论是否向用户收费,项目监管部门都必须重新评估,在升级工作中,项目组对公司的贡献是否有所增加。客观地说,类似的软件升级在任何情况下都不大可能将项目的实际收益提高多少,大多数公司都会视其为正常的保内维护工作而免收用户的升级费用。但是,因为出现了“代码地震”问题,项目组耗费了六人四周的时间才完成修改工作,该项目的成本和费用陡然增加,项目组的“贡献率” 相应地就会大幅下滑。这样一来,项目组成员年终收益的减少也就在所难免了(在这里,我们评价的是项目组对公司的贡献而不是其工作的辛苦程度)。 也就是说,我们必须在设计和开发过程中严防“代码地震”的出现,这不仅能减轻项目的维护工作量,也能切实保障项目组成员的奖金收益。 3 案例分析 还是回到技术问题上来吧。简单地说,案例中的“银证通”系统在体系设计上并没有太大的问题,系统依靠Web Service 等通用接口技术建立的分层构建、组件独立、分布式部署的基本模型也无可指摘。从项目组的修改过程看,小小的需求改动之所以能引发可怕的“代码地震”,这主要是由于系统中存在着大量的代码重复。 Kent Beck 和Martin Fowler 将“重复的代码(Duplicated Code)”视为代码中的“坏味道”之一①。通常,代码重复意味着错误和风险的重复,当需求变更发生的时候,它必然导致修改工作量、修改成本和修改风险的成倍增加。 Martin Fowler 在《重构》一书中列举的代码重复还仅限于方法与方法之间、类与类之间、算法与算法之间的代码重复,这些常见的代码重复问题可以通过“提炼函数”、“提炼类”、“值域上移”、“塑造模板函数”、“替换算法” 等《重构》一书推荐的方法或手段加以解决。但是,在案例中的“银证通”项目里,代码重复的问题已经深入到了软件开发的每一个层面。项目组碰到的代码重复问题实际上涵盖了数据库和应用程序间代码重复、模块之间的代码重复、组件之间的代码重复、界面之间的代码重复、分布式系统间的代码重复等更为复杂的情况,以至于数据库结构的微小调整就足以牵一发而动全身,将项目组毫不留情地拖入“代码地震”的梦魇之中。 为了从根本上解决复杂系统内的代码重复问题,项目组必须在设计和开发的全过程中,有针对性地、分门别类地预防和管理每一种潜在的代码重复风险。下面,我们将以“银证通”项目为例,逐一列举项目中存在的主要代码重复现象,并讨论每一类问题的预防与解决办法。 数据库和应用程序间的代码重复 “银证通”项目的两个服务程序都需要访问各自的数据库。每个服务程序都有一个相对独立的数据访问控制组件,以便隔离业务功能与数据操作。具体的编码方式是这样的:每个数据库有一套初始化脚本(用SQL 语言编写),