孔子说:“学而不思则罔,思而不学则殆。”从我做项目的经历,我深深感叹古人的智慧。
去年我毕业设计时候做的一个项目是毕业设计管理系统。当时老师给我们的要求是用Jsp 和 Javabean来实现。我主要负责项目建模和Javabean部分,后来负责数据库的同学被SARS困在家不能返校,所以数据库部分也由我来完成了。当时对java的知识是非常贫乏的,只有简单的语法基础,连jsp、 javabean都是第一次听到过。不知道从哪里开始,于是就研究老师给的一个非常简单的演示项目,首先觉得javabean是个比较简单的东西,于是就先开始分析用户需求。
需求怎么来描述呢?由于学校教育的局限,加上自己研究了很长时间高数,政治,英语(干什么用大家都明白吧),造成了知识的严重匮乏。我就以参加毕业设计人员为中心,建立他们的需求列表。但是这样的需求描述是非常片面的,几乎都是静态的需求,而且很难设计几个用户的交互过程,和某些状态的改变。而且这种需求是否有真正符合需要是个问题,所以我立刻和同学一起设计web界面,目的很简单,因为用户的需求都是通过界面来实现的,对界面的设计能涵盖最终提交用户的功能,而且还能发掘很多非用户的需求。这样我在实现这些功能的时候会有数。在一年后当我仔细看过用例分析,uml后感觉是如获至宝。心头的结全部打开。
建模该怎么建?当时真是什么概念都没有,虽然知道uml,但是仅仅知道皮毛。于是我就很简单的从对象的角度考虑问题,首先找毕业设计管理系统中涉及的人员,这个是很明显找出来的,涉及到学生,教师,管理员,进一步提取出项目对象,各个人员之间通讯的消息对象,毕业设计文档对象,等等。接下来设计数据库表,由于学校教数据库的时候只有讲述了ER图到关系模式的转化,而根本没有讲ODL到关系模式的转化(现在看来,ODL应该更好,对理解O/R Mapping有更多的帮助。)于是就画ER图,建立各种表,设定主键,外键,等等。
我遇到的首要问题就是学生,教师,管理员该怎么办,每一个对象建一个表,似乎也没有什么问题。但是在建立消息对象的时候就有问题了,因为消息是要在学生,教师,管理员任意两者都能传送的,我建消息表的时候是这样设计的:一个消息的内容,消息的发送人,消息的接收人(消息内容和发送人一个表,为解决冗余接收人单独一个表),这样这个消息就有很大的灵活程度了,消息发送人可以是三种角色间的随便一种,更进一步,这样的消息可以是系统生成的消息,而且接收者可以是随便哪种角色。但随之带来的问题是:消息发送人、接收者该去参照谁呢?显然填一个发送者、接收者的id是不够的,必须连发送者的角色也填上。第二种方法,每个角色有一个收到消息的列表,让它去参照单一的消息表id,这样也能解决问题。第三种方法,把学生,教师,管理员合并成一个用户表,那么消息发送人,接收人只要简单的参照一个通用的用户id了。这种合并子类的方法会造成教师,管理员的行有些null值,但是考虑到教师和管理员的数目远远小于学生的数目,所以这样的冗余是忽略不计的。最后我还是选择了最后一种方法。不过我还是觉的是对象模型向关系模型的一种妥协。
接着我着手设计javabean,访问数据库用的是单纯的JDBC。因为我对前台界面要访问一个对象的哪些属性是不了解的,事实上也不该了解。所以我只能从数据库取出一个完整的对象,让他来决定究竟要访问哪些属性。而且对对象的修改也是这样,前台修改好新的对象的属性,我来进行更数据库的同步。于是我的javabean就出现了这样的方法:load()、 store()、 update()、delete()这样的对象和数据库表同步的方法。这在一年后的今天看来,实际上我已经自己做了一个O/R Mapping的工作了。当时自己做这样的O/R Mapping是相当痛苦的事情,而且用的方法也是最粗浅的,就是整个对象更数据库同步,即使没有修改的属性也同步,所以要同步的话,必须要先把对象从数据库取出来,修改后再写回。而且要求所有属性必须是nullable的non-primitive类型。第二个问题,对表中的外键怎么反应到bean中?比如每个学生有一个辅导教师,在数据库中很明显学生表需要一个外键参照教师id,但是在StudentBean中教师属性是写Teacher对象呢,还是Teacher id值呢,按照面向对象,很明显应该Teacher对象,但是我就觉得这是个多么heavy的事情呀!再想想教师情况,一个教师有一个Collection的Student对象是多么“重”!意味着load一个教师要load所有的students。于是我采用的方法是:还是用对象,比如teacher 的学生属性,还是一个collection Student对象,同时提供了一个loadStudents()的方法,load teacher对象的时候并不load 学生属性,只有bean的用户显式调用loadStudents()的时候才会加载一组student对象,这在现在看来似乎我自觉不自觉的实现了lazy loading?
现在审视当时编写的javabean,犯的最大最大的错误就是把data access object和business workflow混在一起了。比如把教师所有要调用的功能都放在教师对象里面,而有些功能有时是要涉及几个bean的。现在看了简直是惨不忍睹的设计,虽然当时也困惑过,但是却没有动动脑筋来解决,我现在看了session bean 的思路时,觉得是那么的舒畅、自然。
当时困惑的还有关于jsp的处理,jsp只是负责显示的,但是为什么一个jsp提交的数据要给另外一个jsp去处理呢?这是很不舒畅的做法,于是有了最初的servlet做控制器的想法,但是当时顾不了研究那么多,也没有最终实现,毕竟前台不是我负责的。当我现在知道了Structs,MVC Model2的时候,我以前的困惑都随之解决。又一次感觉非常舒畅。
以上是我一个新手的第一个项目的一些情况,是在非常闭塞的环境里面做的,上网都是电话卡拨号,根本没有接触到主流技术,但是正是这种环境下积累的无数困惑,使我遇到EJB、Hibernate、Structs的时候如饮甘露,迅速的吸收了他们的营养。否则我想学习这些技术无疑是很耗时间的,因为根本没有更这种技术产生共鸣,不知道他们搞那么多框框究竟想干什么,而我产生了一种共鸣,他们这些框框,正是开我心头疑惑的锁。
又回到开头孔子的话,事实上我在做项目中,一直都是按自己的思考方式在“思”而没有去“学”,所以常常感到疲倦而无所得,即“殆”。但是如果不实际自己去动手做,而光光学j2ee,必然会很迷惑,即“罔”。我感觉先有困惑再有解决,是掌握技术的十分有效的而且巩固的方法,而且很有创造性,是属于归纳的思考方式。所以碰到问题,首先需要想想按常理该怎么去解决,再去寻找别人怎么解决的,这样自己提高会十分迅速。
现在我正在用EJB、Structs来重写那个项目。虽然我对整个需求已经相当清楚,毕竟有了第一个项目作为原型,但是我还是试图使用比较规范的方法来设计。首先分析了很多use case,并且使用Rational的RequisitePro来跟踪需求的变化。接着又使用Rose对use case画了use case diagram。对关键对象交互过程画了 sequence diagram,对某些对象的状态画了state diagram。最后很自然的导出了最重要的class diagram。
我现在最大的困惑在Entity Bean上面,建模的时候很自然会有一个User对象,Student, Teacher, Manager 对象继承自这个User对象。那数据库表怎么设计呢?第一种,三个对象三个表,对应StudentBean TeacherBean ManagerBean三个EntityBean。第二种,三个对象一个表,只有一个UserBean。第三种,把User单独一个对象,那么就是四个对象四个表,这样一个子对象,要映射两张表EntityBean不行吧。
暂时不管究竟用什么设计,来考察一下文档对像DocBean,我们主要看看它的cmr部分,一个cmr是author 属性,它应该是三种角色对象中的一种,问题来了,如果三个对象三个表,那么我这里的cmr怎么写?
public abstract StudentLocal getAuthor(); ?
public abstract TeacherLocal getAuthor(); ?
public abstract ManagerLocal getAuthor(); ?
考察第二种,三个对象一个表,只有一个UserBean。那这个是相当简单的只需要
public abstract UserLocal getAuthor();
那最后只有第二种方式才能解决问题,就是只有一个User对象,于是我为了使用EntityBean不得不对我的模型进行修改,把原来清晰的三个对象揉合到一起,虽然说以后某个student变成了teacher 或者manager可以很方便的升级,但是这种情况在我这个例子里面是很少有的,而且数据库这种合并子类的方法给teacher,manager形成了很多的null值,虽然这是忽略不计的,但是总不是优雅的做法,最关键的是我的对象模型又一次向关系模型妥协了。
CMR的另外一个问题是,要CMR必须把所有相关的Bean放一个包里面,在一个xml文件里面配置,我的项目里面的entity bean都是要更user有关系的,那我都需要把这些bean打成一个包,用一个xml描述!我的还是小项目,如果一个项目有100个entity bean,这些entity bean都是相互关联的,那我要把这100个entity bean放在打成一个包,用一个xml配置!那这个xml文件该有多长!一个小小的错误全部完蛋。
看来Entity Bean确实如很多人说的那样不是十分灵活,我也开始倾向于用hibernate来做持久化了,但是我需要有足够的灵活性,我想继承和多态的支持还是很重要的,这样才能真正是“object-oriented domain models”方式,而不是以数据库表为中心的方式,引用Hibernate的一句话“Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier”
我对AOP也是十分关注,因为它实在是太激动人心的概念了,有了AOP那么我们还要容器干什么,容器的功能完全可以通过AOP来实现,就像Spring框架,除了没有分布式外几乎都能支持吧。而jboss4.0也已经通过AOP实现了。考察我的项目,我发现有一个这样的需求需要满足,就是:在执行某些business logic之前必须要检测对这个方法的调用是否超过期限了,比如学生去选择研究课题,如果选择过了就不能再次选择,虽然这个方法可以由前台很简单的解决,但是我觉得在业务逻辑层防止这种行为的发生是业务完整性的一个部分。而通过AOP,把这样一个很多方法都要调用的公共功能作为一个aspect是很好的。我的问题是在容器中运行的EJB能够用AOP吗?在Jboss4.0里面各种EJB都是通过AOP方式来提供服务的,似乎我自己多加一层业务层面的服务应该是可行的(使用jboss aop),但是这样的EJB放在别的容器里面运行会怎么样?会影响到容器对EJB的干预吗?请各位前辈指点。
posted on 2005-10-27 12:48
rkind 阅读(145)
评论(0) 编辑 收藏 所属分类:
学习方法与经验