我使用OO技术第一次设计软件的时候,犯了一个设计者所能犯的所有错误。那是一个来自国外的外包 项目,外方负责功能设计,我们公司负责程序设计、编码和测试。
第一个重要的错误是,我没有 认真的把设计说明书看明白。功能点设计确实有一些问题,按照他们的设计,一个重要的流程是无法实 现的。于是我在没有与投资方沟通的情况下,擅自改动了设计,把一个原本在Linux系统上开发的模块改 到了Windows系统上。结果流程确实是实现了,但是很不幸,根本不符合他们的需要,比起原先的设计差 的更多。在询问了这个流程的设计意图之后,我也清楚了这一点。对方的工程师承认了错误,但是问题 是:“为什么不早说啊,我们都跟领导讲过了产品的构架,也保证了交货时间了,现在怎么去说啊 ?”。他们设计的是一个苹果,而我造了一个桔子出来。最后和工程师商议的结果是:先把桔子改 成设计书上的苹果,按时交货,然后再悄悄的改成他们真正需要的香蕉。的这时候距离交货的时间已经 不足三天了,于是我每天加班工作到天明,把代码逐行抽出来,用gcc编译调试。好在大部分都是体力活 ,没有什么技术含量,即使在深夜大脑半休眠的情况下仍然可以接着干。
项目中出现的另外一个 错误是:我对工作量的估计非常的不准确。在第一个阶段的时候,按照功能设计说明书中的一个流程, 我做了一个示例,用上了投资方规定的所有的技术。当我打开浏览器,看到页面上出现了数据库里的 “Tom,Jerry,王小帅”,就愉快的跑到走廊上去呼吸了一口新鲜空气,然后乐观的认为: 设计书都已经写好了,示例也做出来了,剩下的事情肯定就象砍瓜切菜一样了。不就是把大家召集起来 讲讲设计书,看看示例,然后扑上去开工,然后大功告成。我为每个画面分配的编码工作量是三个工作 日。结果却是,他们的设计并不完美,我的理解也并不正确,大家的思想也并不一致。于是我天天召集 开会,朝令夕改,不断返工。最后算了一下,实际上写完一个画面用的时间在十个工作日以上。编码占 用了太多的时间,测试在匆忙中草草了事,质量……能掩盖的问题也就只好掩盖一下了, 性能更是无暇顾及了。
还有一个方面的问题是出在技术上的,这方面是我本文要说的重点。按照 投资方的方案,系统的主体部分需要使用J2EE框架,选择的中间件是免费的JBoss。再加上Tomcat作为 Web服务器,Struts作为表示层的框架。他们对于这些东西的使用都是有明确目的,但是我并不了解这些 技术。新手第一次进行OO设计,加上过多的新式技术,于是出现了一大堆的问题。公司原本安排了一个 牛人对我进行指导,他熟悉OO设计,并且熟悉这些开源框架,曾熟读Tomcat和Struts源代码。可是他确 实太忙,能指导我的时间非常有限。
投资方发来设计书以后,很快就派来了两个工程师对这个说 明书进行讲解。这是一个功能设计说明书,包括一个数据库设计说明书,和一个功能点设计说明。功能 点说明里面叙述了每一个工作流程,画面设计和数据流程。两位工程师向我们简单的说明了产品的构想 ,然后花了一个多星期的时间十分详细的说明了他们的设计,包括数据表里每一个字段的含义,画面上 每一个控件的业务意义。除了这些功能性的需求以外,他们还有一些技术上的要求。
为了减少客 户的拥有成本,他们不想将产品绑定在特定的数据库和操作系统上,并且希望使用免费的平台。于是他 们选择了Java作为开发语言,并且使用了一系列免费的平台。选用的中间件是JBoss,使用Entity Bean 作为数据库访问的方式。我们对Entity Bean的效率不放心,因为猜测他运用了大量的反射技术。在经过 一段时间的技术调查之后,我决定不采用Entity Bean,而是自己写出一大堆的Value Object,每个 Value Object对应一个数据库表,Value Object里面只有一些setter和getter方法,只保存数据,不做 任何事情。Value Object的属性与数据库里面的字段一一对应。与每个Value Object对应,做一个数据 表的Gateway,负责把数据从数据库里面查出来塞到这些Value Object里面,也负责把Value Object里面 的数据塞回数据库。
按照这样的设计,需要为每一个数据表写一个Gateway和一个Value Object,这个数量是比较庞大的。因此我们做了一个自动生成代码的工具,到数据库里面遍历每一个数 据表,然后遍历表里面的每一个字段,把这些代码自动生成出来。
这等于自己实现了一个ORM的 机制。当时我们做这些事情的时候,ORM还是一个很陌生的名词,Hibernate这样的ORM框架还没听说过。 接着我们还是需要解决系统在多种数据库上运行的问题。Gateway是使用JDBC连接数据库的,用SQL查询 和修改数据的。于是问题就是:要解决不同数据库之间SQL的微小差别。我是这样干的:我做了一个 SqlParser接口,这个接口的作用是把ANSI SQL格式的查询语句转化成各种数据库的查询语句。当然我没 必要做的很全面,只要支持我在项目中用到的查询方式和数据类型就够了。然后再开发几个具体的 Parser来转换不同的数据库SQL格式。
到这个时候,数据库里面的数据成功转化成了程序里面的对象。非常 好!按道理说,剩下的OO之路就该顺理成章了。但是,很不幸,我不知道该怎样用这些Value Object, 接下来我就怀着困惑的心情把过程式的代码嫁接在这个OO的基础上了。
我为每一个画面设计出了 一个Session Bean,在这个Session Bean里面封装了画面所关联的一切业务流程,让这个Session Bean 调用一大堆Value Object开始干活。在Session Bean和页面之间,我没有让他们直接调用,因为据公司 的牛人说:“页面直接调用业务代码不好,耦合性太强。”这句话没错,但是我对“业 务代码”的理解实在有问题,于是就硬生生的造出一个Helper来,阻挡在页面和Session Bean中间 ,充当了一个传声筒的角色。
于是在开发中就出现了下面这副景象:每当设计发生变更,我就要 修改数据库的设计,用代码生成工具重新生成Value Object,然后重新修改Session Bean里面的业务流 程,按照新的参数和返回值修改Helper的代码,最后修改页面的调用代码,修改页面样式。
实际 情况比我现在说起来复杂的多。比如Value Object的修改,程序规模越来越大以后,我为了避免出现内 存的大量占用和效率的下降,不得不把一些数据库查询的逻辑写到了Gateway和Value Object里面,于是 在发生变更的时候,我还要手工修改代码生成工具生成的Gateway和Value Object。这样的维护十分麻烦 ,这使我困惑OO到底有什么好处。我在这个项目中用OO方式解决了很多问题,而这些问题都是由OO本身 造成的。