CREDIT项目阶段反思

FuqiangWang


2014年从msn space存档中重新恢复出来!

CREDIT从2004年开发至今,也快2年的时间了,现日方想对这个系统的SERVICE进行提取总结以便以WEB Service的形式开放并贩卖,所以,之前的系统在某种意义上已经处于维护阶段,当然也会时不时添加新的功能,但开发的主体从现在开始应该转向web应用的开发上去,在这种背景下,有必要对原来的系统从设计到实现上给出一个反思,以便更好的进行以后的工作,提出系统的缺陷不是为了诋毁当前系统,而是在这些缺陷的基础上,给出一些反思和解决方案,从而在以后的系统设计和实现中更好的改进并消灭类似的问题点,进而可以打造更加健壮,更加易于维护的系统。

因为思绪较为跳跃(好听一点儿就是活跃,呵呵,其实就是意识流),所以,罗列的条目可能不会按照一个较为有条理的顺序展示;同时,可能个人经验和阅历上的不足,以下罗列的可能是个人的偏颇见解,全作不完全参考。

数据库设计

当前情况

数据库schema的设计,从业务上来说,应该没有什么大的缺陷,因为是日本人给出的设计,并且他们对于业务较为熟悉,所以,没有太多纰漏;但,这并不是说他们设计的数据库schema就是完美的,像今天上午讨论的事件表和贷付禁止表,应该说就存在问题,尤其是贷付禁止,个人感觉,当前的设计纯粹就是一个履历表的作用了;

除此之外,虽然日本人在后面陆续加入了一些Timestamp型的字段来记录一些操作信息,但这些字段除了这个作用,也就没有起到其他作用了,尤其是,各个表在根本没有考虑到最基本的乐观锁的概念,从而导致某个时期同步问题很多,尤其对金额字段进行操作的时候,虽然说通过某些手段暂时解决,但也不可避免的存在某些风险,比如陈旧数据覆盖最新数据等情况。

Alternative

针对同步的问题,添加乐观锁,比如hibernate就提供了对该概念的最基本支持;

像贷付禁止表的问题,应该分化该表的作用,重新为其添加履历表,而不是将禁止和解除的记录全都放在这里,解除和禁止的时候添加和删除相应记录,并添加到履历表,分化贷付禁止表的作用;

其他的就不好再说了,毕竟,我也没做过太多DB Design,呵呵

系统基础架构

当前情况

2004年项目刚开始的时候,徐敬琪搭建的完全基于SWT的底层框架,提供了窗体等的生命周期管理和他们之间交互的消息机制,所有窗体需要实现InnerFrameClient自定义接口,以便底层框架可以控制其生命周期中的各种situations;总得来说这个底层架构打的挺成功,就是添加窗体的话,可能配置的地方很多,在操作上有些繁琐。

Alternative

其实,我们开发CREDIT的时候,Eclipse已经是3.0.1的版本了,当时RCP(Rich Client Platform),已经可以用于实际生产环境,但当时可能开发进度太紧,更不没有足够时间调研,而且当年也是刚接触SWT/JFace,所以,没有发现这个好东东,如果以后开发类似应用的话,完全可以使用RCP作为底层架构,没有必要再自己开发一套出来,而且,自己开发出来的想要重用也很困难,需要花费更多精力去重构他,有如此好的Wheel可以用,何乐而不为那?!

版本的更新

当前情况

系统每次添加新的功能之后,通过了开发人员和测试组成员的完全(其实根本不可能)测试之后,需要使用InstallAnywhere来重新打包发布新的安装文件,之后将安装文件部署到发布的Location供操作员下载并重新安装使用;每次繁琐的发布过程暂且不提(使用我后来提供的Ant脚本后,繁琐程度降低了不少),光是源文件和各个版本之间的管理就够人头疼的,虽然每次发布完版本后,现在都是打上tag,但是,依然存在一定风险,比如,从当前tag开始,开发人员已经开始了下面新功能的开发,而每次源代码编写还没有经过测试和核对的情况下,大部分开发人员就将这些代码commit到cvs,而且源代码文件很多,当因为进度吃紧,而只是想发布一部分新的功能的时候,开发人员更不就分不清那个源文件该替换,该替换的源文件又应该从那个commit替换回来,往往将整个的package都update一遍,这样就有可能导致功能和数据库schema等不同方面之之间的不一致问题的产生。到现在没有出现太大的问题,个人感觉实在是万幸。

Alternative

其实,我们可以使用Java Web Start来部署我们的应用,这样,就不用说每次都重新打包发布新的安装文件,而通过顶点来统一客户端的部署和更新;如果这种方式还不能满足需要的情况下,同时底层框架又使用RCP的话,我们可以使用RCP的update机制(也就是Eclipse的update机制),每次有新的功能发布,我们将这些新功能实现和打包为不同的plugins,并部署到update Site上面,这样,也可以很好的解决同一部署和更新的问题,而不用劳烦操作员动不动就需要重新安装新的客户端,也可以减少人为的错误。

系统配置的管理

当前情况

所有的配置文件,不管什么格式的,properties或者xml格式的,没有一个统一的访问接口,比如xml通过我们自己实现的Configurator来读取,properties直接通过Properties类load进来等等,当然,像我们的Configurator类通过xpath来处理资源的读取可以很好的处理其请求,但,我们没有处理多个配置文件统一访问的情况,而且,如果将不同的配置需求分开,本身可以更便于管理。

Alternative

可以对当前的Configurator等配置utilities类进行整合,以提供不同配置文件的统一访问机制,但因为Jakarka Commons Configuration实际上已经为我们做了这部分工作,所以,还是不要再自己发明轮子的好。通过Configuraton接口,可以对ConfigurationFactory加载的不同配置资源进行统一的访问,岂不easy?!不过,我在作demo的时候发现,好像他对于XML的attribute的访问有些限制,当然,可以通过其他方式解决;

除此之外,如果使用RCP,我们可以通过他提供的Preference机制来进行系统的配置。

质量保证

当前情况

当前的CREDIT的质量保证,可以说,开发人员并没有起到太多他应该起到的作用,完全由测试组人员担当了最主要一道也是最后一道质量防线,开发人员在将功能编码调试通过之后,通常就直接将系统仍给了测试人员进行测试,这无法为测试人员增加了太多的负担,而开发人员在忽视了自身的一些职责,因为,软件的质量的第一步也是最基本的质量考核应该从开发人员的代码开始进行审核,代码是所有质量保证的基础,没有高质量的代码,也很难说能够有高质量的系统,这就好比购买笔记本一样,为什么那么些人宁愿多花银子购买ibm等国外厂商的产品而不愿购买国内更为便宜的本子那?!其实,笔记本的基本构件和原理都差不多,但是,国外能够生产出高质量的构件,并将这些构件以严谨科学的态度实验后组装在一起,但国内的厂家可能就只是买回来构件,不管这些构件是否兼容就直接组装出售了(没有贬低国内厂家的意思,我也希望国货自强,但首先应该接触浮躁)。

Alternative

针对开发流程进行改进,让开发人员充分发挥其主导作用,与其让测试人员帮你被动的防御,还不如我们主动的防御来的效果更好一些。例如,使用TDD(Test-Driven Development)进行开发,每次在实现一个新的功能点或者单个的功能类的时候,我们首先编写针对这个功能点和类的单元测试(Unit-Test),以单元测试作为我们思考的Prototype,并不断加以重构和改进(要知道,单元测试是使用我们功能类的第一个地方,在这里,如果这个功能类易于使用,设计优良,那么,将这个类加入到你的系统之后,他可以以同样的高姿态展现自己并发挥作用),而不是说当这个功能点或者功能类开发完成之后,为了验证才去些他的单元测试,这个时候,你的头脑中已经下意识的形成了一个观念,你写的Unit-Test也只是对你认为的流程进行的测试,即使有什么问题,你也不会测出什么结果的,这样的单元测试或许会偶然发挥作用,但已经没有什么意义了。

所以,有了单元测试作为第一重保障之后,开发人员就可以对功能类大胆的进行重构和改进,因为你有自己的防线啊!在所有的代码通过Unit-Test之后,我们就可以move到下一步,进行系统的集成测试等,当这些白盒测试都通过之后,我们才会将我们信心十足的系统开放给测试组人员,让他们为系统作黑盒测试,为系统的质量保证和安全奠定最终基点。

认证和授权

当前情况

从JAAS(Java Authorization Authentication Service) Adapt过来的一个自定义解决方案,对于认证方面的需求要求不是很高,所以基本满足要求,但授权方面,可能策略较为复杂,也不能很好的处理,所以,这个框架没有发挥完全的作用,但现在工作的很好。

Alternative

我现在也没有想出更好的替代方案,第一印象当然就是JAAS啦,除此之外,像基于Spring的Acegi安全框架啦,OpenSymphony的OSUser(未发布正式版)啦等等,都不是可以很好的集成到现在的系统中,或者说很好的集成到Standalone形式的应用中,如果其他人有更好的建议,希望可以分享你的观点。

系统异常体系

当前情况

无论是从数据访问层来看还是从业务层来看,CREDIT系统都没有一个设计合理的异常体系结构来支撑,或者说根本就没有什么异常体系;数据访问层只是单纯的以单一的一个自定义的DaoException(Checked Exception)来向上层抛出,业务层根本就无法根据异常来判断应该进行什么样的处理,进而导致在业务层也以单一的自定义ServiceException来标志业务处理异常,可以想像,最终导致的结果就是以粗暴到何种简单的程度向用户进行反馈,“系统发生内部错误,请与系统管理员联系…”

Alternative

对于一个设计良好的系统来说,一个好的异常体系可以为系统的健壮性提供很好的保证,如果系统可以重新实现的话(当前系统因为力包稳定,而且重构的范围很大,导致困难重重,所以,基本不存在重构的可能),在数据访问层,可以考虑引入Spring提供的数据访问层现成的异常体系,或者根据情况,给出一套自己的实现也可以;而业务层则需要结合CREDIT系统业务逻辑和当前系统情况,从总体上给出一个设计良好,分类详细的适合当前系统的业务层异常体系结构,使客户端代码可以根据这个良好的体系结构为用户提供相应的反馈,使得系统的健壮性和用户友好度可以更进一步。

数据访问

当前情况

徐敬琪针对hibernate和jdbc两种数据访问方式给出不同的父类实现,子类要继承相应的父类以便访问相应资源,目前系统中使用并没有出现什么大的问题,但个人在使用中依然能感觉有些不便之处:不管使用哪一个实现类,只要你使用他,就必须记得dispose掉,否则数据库资源就无法释放了,像资源释放等问题,我想不应该扔给调用方来关注;在jdbc的数据访问类中,你依然要处理那些与你的需求基本不相关的Statement,Connection等,这无疑增加的代码的出错几率,而且不易维护(较多的分散)。

Alternative

使用Spring提供的HibernateTemplate和JdbcTemplate可以很好的进行数据访问操作,无须开发人员较多关注不必要的关注点,像资源释放,Connection和Statement等的取得等问题,统统屏蔽,对调用方保持透明,使调用方可以更多关注于数据逻辑的处理。

事务控制

当前情况

在HibernateDao和JdbcDao数据访问对象的基础上,提供基于Session,Connection一级的事务控制,开发人员在事物的开始和提交等事物点的处理上不尽统一,造成代码混乱,很不容易维护,这我是亲眼目睹的,真的可以用惨不忍睹来形容;

Alternative

可以使用Spring提供的事务抽象层,以统一的方式管理事务控制,虽然针对Hibernate和Jdbc,Spring提供了PlatformTransactionManager的两个不同实现:HibernateTransactionManager和DataSourceTransactinManager,但是,前者可以统一管理后者的事务,所以,依然较为单一的方式管理事务。

事务管理一级只限制于业务层,Data Access不应该纠缠于事务,针对不同的业务层Service,根据业务粒度,可以通过暴露相应的事务控制方法或者暴露相应的业务实现类来统一事务的管理策略,总之,Data Access层绝对不在应该掺和事务管理代码,一切事务现在全部归业务层统一管理。

Job Commit

当前情况

对于一些长时间运行的job,通常的做法当然是为其另起一个线程,所以,CREDIT中徐敬琪自定义提供了IProgressTask类作为job的实现接口,并提供一个job运行的context,即IProgressTaskSubmitter。由IProgressTaskSubmitter来处理job的运行,其实原理没有什么特殊之处,但在具体使用上个人认为存在一些不便之处:首先,异常处理不是很优雅,通过int型返回值来表明不同的job运行结果和状态,显然不如强类型的异常更能表明问题;其次,不管job是成功了还是失败,都需要开发人员释放workbench的忙碌状态等,显然,“包装”力度不够,开发人员不应该关注他们不需要关注的关注点;如果使用不当,还可能造成死锁,我碰到过类似情况。

Alternative

其实,当前的实现有reinvent the wheel之嫌,因为JFace已经提供了ProgressMonitorDialog,通过这个dialog来run接口IRunnableWithProgress的实现类job就可以了,如果觉得说他有些方面不能定制或者还不能满足你的要求,那你也可以通过直接使用org.eclipse.jface.operation.ModalContext类来run相应的job实现也可以。 10-表格数据的显示和处理

表格数据的显示和处理

当前情况

使用徐敬琪自定义开发的XTable组件现实表格类的数据,主要是SWT的table不能很好的按照日方要求表现相应的数据,使用XTable,可以基本满足日方的数据表现需求。但在个人使用过程中依然觉得存在一些瑕疵,比如,表格定义的时候,通过反射表明数据和表格之间的对应关系,造成定义XTable的时候较为繁琐(当然,copy Code的方式可以省去不少烦恼),而且,数据对应是通过方法明和返回值类型的对应实现的,稍微不甚就会频发异常,这在CREDIT组员中是经常碰到的现象; ## Alternative 如果要满足日方数据的表现需求,现在实在没有太好的选择,只有自己去自定义Table实现,现在虽然SWT/JFace的table有了一定的增强,但某些方面依然很弱,比如Swing中Table的Render功能,SWT/JFace的Table对这方面的支持就很少,或者说根本没有什么支持(自定义除外)。当然KTable作为SWT/JFace的一个自定义Table也提供了许多自定义功能,但还是有些地方不尽如人意,不过,大多数情况下已经很可以了。


恩,目前就想到这些东西,如果有新的,随时补充…

以上愚见,欢迎拍砖!


>>>>>> 更多阅读 <<<<<<


欢迎加入「福强私学」

跨越2190个日夜,始终坚持“实践 + 原创”打造的715125字专属知识库,囊括了(但不限于)从职场、技术、管理与商业等多个板块的内容。

  • 一个ChatGPT触达不到的地方
  • 一个带你超越AI/人工智能的地方
  • 一个与你一起成长的地方

https://afoo.me/kb.html


开天窗,拉认知,订阅「福报」,即刻拥有自己的全模态人工智能。

订阅「福报」