这一周是忙碌的。
需求分析基本完成了,use-case图几经修改,我们在绘制use-case之前进行过多次的讨论,试图完全明确所有的功能,然后再绘制use-case,企图一次成功,现在回过头看这个想法太天真了。不过经过充分的讨论,第一版的use-case已经基本符合要求了。大概是我们对use-case认识还不是十分明确,做出来的图比较简单,主要体现在各个用例之间似乎没有多大的联系,当然也可能是这个系统本身太简单的缘故。其实在刚做完use-case时,我们都以为这只是应付老师的一个图表罢了,对于项目过程而言没有多大的帮助作用,我仍然记得绘制图表的原则:只绘制对今后有用的图表。然而后面的分析设计中,这个use-case图却真正成为了我们的工作准则。因为这是一个经过充分讨论的成果,得到了所有人的认可,在后面的分析设计中,碰到过一些有争议的地方,一切符合use-case成为了事实的标准,然而这多少成为了一种约束,当然这种约束可能是有益的和必需的。不得不承认的是,这个use-case终究还是有一些遗漏的功能,然而我们后来在分析设计时发现之后,大家都不愿意再修改use-case了,因为相关的文档已经完成,加上时间紧迫,于是只能去掉那些可要可不要的功能,我们的借口就是:要符合use-case。这样做,我们事实上还是在采用瀑布模型,如果按照迭代的方法,这里是应当对需求分析进行修改的。幸好我们没有真正的客户,否则显然不能这么糊过去。自然的,我们失去了一次训练需求变更的机会,有些遗憾。反思为什么会这样:可能可以归结为时间不足,进一步考虑大概是因为进度表制定的不合理,再进一步考虑原因也许在于系统的范围还是定得太大了一点,使得我们的训练广度是保证了,然而精度不够。看来学生项目出于训练的目的,范围应当尽可能的小,甚至是一个HelloWorld都可以接受,因为项目的目的是尽可能熟悉和掌握软件工程的各个环节,软件本身不用很强大,想起一句话:麻雀虽小,五脏俱全。一个小的软件已经足以训练大部分的内容了。
需求文档也完成得很顺利,有了use-case一切都变得简单。对每一个use-case要做一个specification,也就是对它的一些简要说明、这个功能的输入输出、前置后置条件等等,这费了很大的功夫,犯过许多错误。一个典型的:我们需要将一部分信息组织成一个记录写入数据库,这个功能的输出(outputs)以及目的地(destination)是什么:起初的考虑是输出为信息,目的地是记录。然而后来发现这样似乎是有些荒唐的,因为输入也是信息,难道输入和输出不经修改是一样的,那这个功能还做了什么事情。后来改为了输出为记录,目的地为数据库,这是大家比较容易接受的结果,尽管我们也不很确定究竟怎么才算准确。想起以前和老师讨论一些问题时老师的一句话:当碰到两难时,选择那种符合大多数人习惯,能让大多数人接受的方案。我想这里是适用的。
之后想起来应当做一份glossary(词汇表,或称为数据词典吧),就是对需求分析中用到的一些词加以解释,这些词可能贯穿于整个开发过程中,有这样一份词典后面看文档的人碰到陌生的用语时有据可查。这份文档做得晚了,我就被一件事情困扰过。我们做需求分析的组员对于“续住”这个功能(还记得我们做的是酒店管理系统吗)用的英文单词是continue,这倒无妨,只是我看文档时对着这个单词琢磨了半天也不能参透这个功能的意义,后来和她交流才知道是续住的意思,试想如果当时就有glossary将省多少事。更可怕的,如果我误解了后果将很严重(其实我起初的理解是由登录窗口进入主窗口,叫做continue,呵呵,幸好和她有一个交流)。
这些工作做完之后,我们认为需求分析的文档就基本成形了,于是我们举行了一次评审,这是一次审查(Inspection),由所有组员参与,评审对象就是需求文档。我们提前两天将文档打印分发给每个人回去浏览准备,一个小插曲是打印和复印的费用花去了我们第一笔集资经费(50元)的一半,让我们吃了一惊,后面要勒紧裤腰带省点用了,毕竟大家都还是学生,花的是自己的伙食费。其实我本来以为这次评审将是失败的,因为大家以前没有过经验,而且这段时间大家都比较忙,可能不会太认真的去阅读文档和找出瑕疵。然而事实让我吃了一惊,每个人都做了充分的准备工作,人均花费了两个小时阅读文档,这已经足够了。我们的评审相当顺利,问题接二连三地被提出来。当然有一点遗憾是评审的时间没有把握好,有点拖沓了,不是因为问题太多,而是花了一些时间在如何解决上。按照各种书籍的观点,评审会议应当着重发现问题而不是解决问题,我们不经意的犯了这个错误。然而这样做是有道理的,因为在完成需求文档时,犯的一些错误多半是对这个问题不理解,而不是不知道该怎么解决,也就是说不是很清楚what,一旦明白了what,那么how可能是比较容易的。所以我们有必要及时指出问题是什么,指出问题的本质,这样长远的看是节省时间的,减少返工的次数。为什么大家会愿意去花宝贵的时间阅读文档和找出瑕疵,我想一个直接的原因是因为打印这些文档的开支比较大,大家都很心疼钱,所以不忍浪费,于是才会认真地阅读文档。挺好的。
起初是我们的“首席架构师”完成系统结构的设计,为了节省时间我们试图同时完成系统架构的设计和模块的细节设计,于是碰到了问题,功能上有点不统一,直接原因是对需求没有做很好的回顾和了解,仍然比较模糊,于是,原型系统发挥作用了。我在拿到需求文档之后,花了一个晚上做了一个原型系统,只有简单的界面,示意一下,这大概也是原型系统的初衷之一吧,原型系统本来是为客户服务的,与客户有一个基本的交流对象。我当时的本意是直接做正式的界面(这个天真的想法是很不合适的,然而我当时相当自信,认为没有问题),而不是做原型系统,后来我们的“评审经理”提醒我说你现在做的应当是一个原型系统,用一下就抛弃的,我才察觉到险些犯了一个巨大的错误。庆幸的,这个系统正好可以被用作原型系统,为我们的分析设计做了很好的铺垫。我们的设计师不是很愿意阅读需求文档,更愿意直接看原型系统来发掘设计思路和要求,幸好这个原型是比较完善的,至少没有遗漏一些功能表示。
然而,第一个设计是失败的,原因之一是对RMI(远程方法调用)没有很好的了解,RMI是这个系统的底层实现,这是我的要求,我的另一个要求是力争符合MVC的标准,或者向着它努力,以它为原则。然而我们的设计师之前没有RMI和MVC的基础知识,就直接进行了设计,显然是不合适的。我们否决之后重新阅读了书籍,建立了第二个架构,模块分得也比较仔细和合适了,基本可以接受,然而整个系统结构显得有些冗余和不清晰,问题出在MVC中的M。我们的理解中,V就是界面,对应于分析类中的boundary方面的内容;C毫无疑问就是主控器了;然而M,都知道是模型Model的意思,起初的思路是寻找系统中的名词,找到之后形成类,也就形成了相应的M。于是,系统中充斥着M。去掉一些不必要的,还是有很多,我隐约的觉得有些不必要。我花了一个下午推翻了重做,建立的设计方案中几乎没有M,或者说实体(Entity)类,这样的方案是简单的:界面发出事件请求,相应的控制器进行响应,然后更新界面并反馈信息,没有实体什么事。这或许不是一个面向对象的系统,因为乍一看找不到对象。然而这样的系统实现起来相当简单和直接,难道是设计的倒退吗,还是或者根本没有弄清楚面向对象的实质。我们的设想是,所有的名词(实体对象)都通过数据库持久性存储起来了,访问或者更改的时候直接查库,然后直接把查询结果以ResultSet(结果集)的形式交给界面去更新就可以了,何必假惺惺的封装起来再给界面,然后界面那边又要解封装,似乎多此一举。我们没有找到这样设计的大缺陷,尽管我们知道这可能是不合适的,比如不利于扩展,不利于代码重用等等。另一个重要的缺陷是,界面部分也要完成一些业务逻辑,比如对结果集信息的提取,这显然是不符合MVC要求的。然而鉴于我们现在的编程水平,易于实现是相当重要的。于是,这个方案被接受了,这是一种妥协,更像是一种失败的妥协,或者说这样的设计更像是一种失败的设计。可能后面还会返工,适当的增加一些M的部分,比如至少不能把ResultSet交给界面吧。呵呵,偶尔想起来太荒唐了。
界面设计方面,仍然存在很大的争议,因为可以参考的系统很多,很难达成一致。这部分工作正在做,我把我的初始意见形成草案分发了出去,大家讨论之后试图在五一前达成一致,因为一旦放假,沟通将变得困难。一个合适的考虑是五一期间基本完成编码,当然前提是大家都对各自要做什么相当清楚,接口的定义要一致和清晰,这部分我们还比较弱,设计还是粗粒度的。我们渴望接下来的几天能够提速,现在也正在提速。尽管我们落后进度将近一周,不过通过加快设计并形成良好的设计方案,我们的编码能够节省很多时间,测试也是一样,大家对按时完成充满了信心。当然,没必要也不可能五个人都参与编码,可能直接编码的只有两三个人,我负责界面,另外一个数据库编程的高手,其他的适时而定,编码方面绝对不是人多了时间就短的,绝对不能用人月除以人数得到月份,我觉得对这样的学生系统,一两个人写代码就足够了。
最后用一句话总结这一周:紧张而充满希望。