邀请dearshor讨论一个模型

<请教初学DDD和四色原型设计的困惑>一帖中,讨论一个考试系统的模型.

经过这几天的细化,以及对UML的进一步理解,重新整理这个模型,虽然还是初级阶段,还是希望dearshor能讨论一下.

模型内容有所扩展,原来只是涉及到题库管理,现在加入了 组卷管理 和 分配试卷 的需求.

组卷就是按照一定的组卷模版(PaperGroupTemplate), 通过ExamPaperGrouper组出试卷(ExamPaper). 考生(Student)登陆系统后,自动分配一份试卷,分配给考生的试卷叫答题卷(ExamAnswerPaper).

目前模型可能还有一些潜在的对象未表达,但是我觉得核心对象已经出现,并且该模型至少能建立一个 考试系统 的原型.具体需要还需要通过原型与客户进行需求细化.

关键是这个UML的类图表达是否正确,至于这个模型是否符合DDD, 我自己还挺模糊的.公司里没人可以讨论,发到论坛上来讨论.


谁能指点一下,如何使用Hibernate映射继承关系.

一个题目Question 有多个子类, 如果使用hibernate进行orm...

>使用Hibernate映射继承关系
使用subclass等配置,具体可查查Hibernate手册。

上图原型细化得很具体了,需要别人和同样进入业务如此深度才能与你讨论,个人以为只要大方向没有错就可以,可以不断细化和迭代。

to BinnyJ:

>如何使用Hibernate映射继承关系.

可以使用JPA标准注解:
@javax.persistence.Inheritance
来实现(即便你不使用JPA,而使用原生hibernate api,也可以使用上述注解,因为hibernate annotation是jpa annotation的超集)

如果一定要使用xml configuration,那么,如banq前辈所说,就用

关于上述类图,如banq前辈所说,你已经做的很细致了,要结合你的具体业务才能做进一步探讨。或者你可以说说还有什么问题困扰着你,大家就此具体问题来一起探讨一下,这样比较有针对性,否则无从下手啊。 :-)

模型里的questionType代表什么意义?
另外感觉模板里的试卷难度,应该用难度系数上下限表达,以匹配问题里的难度系数。并且模板不应该同时确定问题数量么?
to dearshor:
对于这个模型 ,有两个地方个人感觉不舒服。
1.Question父类和 多项选择题目,单项选择题目 ,判断题目子类的继承关系 ,如何映射到关系数据库中。我的初步想法是关系数据库建立两个表 tbl_question, tbl_selectOption

多项选择题目,单项选择题目 ,判断题目子类都映射到 tbl_question 表中。根据questionType(问题类型)来区分题目类型。 题目的选择项(SelectOption)映射到tbl_selectOption. 题目答案(answer)也映射到tbl_selectOption.

问题是,根据我现有的Hibernate的知识,好像出来这个映射关系很奇怪。我想查询所有的 SingleSelectQuestion(单项选择题目),那我要如何查询呢 ?难道要
select * from SingleSelectQuestion where questionType='singleSelect'
如果使用
select * from SingleSelectQuestion
那么 查询出来就不是全部 单项选择题 了。难道hibernate能在xml configuration加入查询条件?

对JPA了解不深, 如果使用xml configuration, 那使用 ,也不知道如何使用。

这几天研究了hibernate的继承关系实现,还没相对想到完美解决方法。

2.就是 考试试卷(ExamPaper), 考试试卷试题(ExamQuestion)
考生答卷(ExamAnswerPaper),考试答卷答题(ExamAnswer)
题目 (Question)
这五个类的关系,看起来就觉得不是很自然。 感觉可能和实际需求有关系, 比如考试试卷试题(ExamQuestion)和 题目 (Question), 是否啥关系。 是组成 或者 一点关系也没有。

由于目前收到需求比较简单,可能无法确定这几个类关系是否正常,也许要和客户交流确定。

to freebox:
谢谢回帖。

模型里面的 QuestionType 是 题目类型属性 ,,区分 题目属于什么类型。 单选题目,多选,判断等。

组卷模版模版内容的确缺少。补上。

组卷模版经过细分后,应该分为 PaperGroupTemplete(组卷模版 ) 和 PaperGroupTempItem(组卷模版条目)

PaperGroupTempItem主要是记录一份试卷有那几个题型组成的. 每个题型有难易系数,数量,每题分数。 见图。


Hibernate多态查询就是自动加条件的,不需要特别指明QuestionType,一旦建立继承映射,它就自动加DTYPE进表里,查询只要依据实体的具体类型就可以了,如from SubOneClass或from SubTwoClass就可以查询One或Two,from SuperClass就把One、Two里的都查出来。我开始以为QuestionType是另有其它用途的,如果只为了标识题目类型,这是不必要的。

我感觉模板应该是筛选题目的一个规格约束,属性由出题人临时派发,或事先设定一批待选模板,出题人选择一个。我觉得这东西是很重要的,出什么样的题、数量、难度等等都由它管理,要依据具体的业务来处理。并且我觉得模板就像是考卷的一个抽象,考卷应该做为它的子类。
class ExamPaper{
public ExamPaper(){}
public ExamPaper(PaperTemplate pt){}
}
如果业务确实是这样的,模板就不用加上时间、标题、分数等等了(比如说grade,IMO就比较难,但IMO里的题就7分,同样一题到了别的试卷里可能变成12分也说不定,但它的难度系数并没有变(如果选题人认识相同的话)),它只管理题目,其它的交给最终的试卷类型.

还有我不太明白您图里的diffcultQuot,我从字面上理解是难度,而我觉得这在模板里体现的话,应该是个区间,比如现在分成五级,难度系数有20种,但这可能并不是平均分配的:
等级1
|-------|
---------------------难度
|---------|
等级2
这里面就有重复的区间,如果要求难度系数完全匹配也可以设定上下限相等,但这可能会查不出结果(等于1.1的可能很少,但位于[1.0,1.2]闭区间的就比较多了,而这些可能是同一等级不同难度系数的题目)。当然这部分是我的推测,具体业务还得咨询客户,也许他们就是只需要精确匹配,那我这样做反而加大模型难度,是不必要的。

还有我一发帖空格排版就没了,等级区间那块复制进文本编辑器应该能看得更清楚。显示帖子的时候为啥不用用pre标记啊?
[该贴被freebox于2009-02-09 13:27修改过]

to BinnyJ:

>1.Question父类和 多项选择题目,单项选择题目 ,判断题目子类的继承关系 ,如何映射到关系数据库中。我的初步想法是关系数据库建立两个表 tbl_question, tbl_selectOption

不要去想建什么数据库表,那是hibernate会为你完成的事(跑一下schema export——也叫hbm2ddl——就搞定了。这不是关键,关键在于你根本不用关心生成的db schema是什么样的)。从现在开始,你的思维里应该只有对象!

这几个对象间的关系比方说就是这样:
多项选择题目 extends Question
单项选择题目 extends Question

那么OR mapping可以是这样:

@javax.persistence.Entity
@javax.persistence.Inheritance(strategy = JOINED)
class Question {...}

其中strategy属性可以指定如下值:
* javax.persistence.InheritanceType.SINGLE_TABLE
每个class hierarchy一张表,就是所有继承自同一个父类的都保存在一张表里,其结果和你上面说的情况类似,不同的是,你不用手动定义一个questionType,也不用为每个对象维护这样一个属性的值,hibernate会为你解决。
See Also: @javax.persistence.DiscriminatorValue

* javax.persistence.InheritanceType.JOINED

A strategy in which fields that are specific to a subclass are mapped to a separate table than the fields that are common to the parent class, and a join is performed to instantiate the subclass.

* javax.persistence.InheritanceType.TABLE_PER_CLASS
A table per concrete entity class


@Entity
class 多项选择题目 extends Question {...}

@Entity
class 单项选择题目 extends Question {...}

查询(以下例子使用JPQL. hibernate完全支持JPQL. 可以说HQL是JPQL的超集):
比如查询所有的 SingleSelectQuestion(单项选择题目):
select q from 单项选择题目 as q
如此即可!千万不要把JPQL(hql)当sql写、当sql理解

> 难道Hibernate能在xml configuration加入查询条件?

确实如此,比如OneToMany,ManyToMany等映射,都可以设置where字句的;还有一些其他的情况,也可以设置查询条件字句,详见hibernate文档。而你这里的继承关系,并不需要通过这样的方式来查询。

关于你提的第二个问题,要看用户的具体需求了。

多谢 freebox 和 dearshor 提供的 hibernate多态查询的资料,根据这些资料我找到有关继承的实现方式。

搞到这里不由让我想起公司以前做过的一个hibernate项目,也算大项目100w左右。但是在该项目根本不存在任何的 继承 关系,因为该项目是面向数据库设计的程序,就象我这个模型里面的QuestionType这个字段,就是延用以前项目的设计,通过字段来区分对象类型,从而回避了继承关系。 而且当时项目是先设计数据库表结构,在根据数据库表生成hibernate,使用hibernate来操作数据库。 呵呵,hibernate的错误使用吧。:0

关于 freebox 提出的 PaperGroupTemplete(组卷模版 )和 考试试卷(ExamPaper)之间的关系, 后来我仔细想了一下,之前的确没考虑好两者之间的关系。

ExamPaper 必然回包含 PaperGroupTemplete 的信息,但是一旦组出 ExamPaper , 那 ExamPaper 和PaperGroupTemplete 之间没任何关系,可以说是根据PaperGroupTemplete 产生了 ExamPaper , 而ExamPaper必须知道是哪个PaperGroupTemplete 产生的, 但是PaperGroupTemplete的修改是不会影响到已经产生的ExamPaper.

比较奇怪的对象关系。

昨天过去和用户探讨了需求后, 发现系统多个地方存在类似 PaperGroupTemplete 和 ExamPaper 的关系。

比如 Question(题目) 和 ExamQuestion(试卷题目),组卷成功后,ExamQuestion必须知道是哪个Question来的, 但是组卷成功后Question的修改不会影响到ExamQuestion.

很明显, ExamQuestion应该保留一份Question的“复制”。ExamPaper也应该保留 PaperGroupTemplete 的一份复制。 个人认为至少 ExamPaper 和 PaperGroupTemplete 不是继承关系。

这样的关系在 类图 应该如何表示?这也是我之前觉得 考试试卷(ExamPaper)、 考试试卷试题(ExamQuestion)、考生答卷(ExamAnswerPaper)、考试答卷答题(ExamAnswer)、题目 (Question)对对象之间关系不自然的原因。

to freebox:
关于 diffcultQuot ,是难度系数 ,昨天和客户交流后,客户暂时没确定diffcultQuot是如何表达。但是客户有提到 freebox所说的难度系数的划分方法。

现在模型设计碰到一个难点。

按我的想法, ExamQuestion 大部分信息从 Question 过来的, 那ExamQuestion 应该重用Question 对象。可能是 包含 或者 组合 关系。

后来想想,由于ExamQuestion 必然是保留Question 的一份“复制”,那就必须有建立一个新的对象来保存这一份“复制”的信息。那这个新对象应该是 Question 类,还是需要建立一个新的 类 来保存。

或者,ExamQuestion里面增加所有Question 的属性, 成为保存这份“复制”的类, 那么ExamQuestion就会和Question 产生重复属性信息,模型看起来有点重复。

大家有啥好的建议吗?我现在脑袋有点陷入“死循环”了。晕 ~~~~

“但是PaperGroupTemplete的修改是不会影响到已经产生的ExamPaper”
我觉得那将是一个新的模板,而已存在并使用过的模板是不允许被修改的,但是如果客户要求允许修改,那就要深入考虑了,可以用组合对象生成考卷。

题目在这里做为属性就要注意了,题目就是题目,不应该带有分数,带有分数的题目是直到出考卷的时候才确定的,完全应该整个ExamQuestion对象出来,而因为ExamQuestion应该没有减少Question的本质,应该可以设计成继承。而像examsubject这类东西我觉得是不应该在question里出现的。那examQuestion里应该有啥呢?我认为除了继承question来的属性,还有examsubject,分值、学生得分等,应该把所有和exam相关的东西都从question中去除,让题目回到题目自己的本质,组卷也只是组装题目,和考卷没有关系,等组装过题目之后才生成考卷,才有分数。就像我以前学习的时候做的练习卷是没分数的,只有在考场里这张卷才会变成考卷,才有分数。

to freebox:

Question必须包含examSubject,因为“题库管理”模块对Question(题目)是以考试科目进行分类管理。新增一个 Question,必须是属于指定的考试科目(examsubject)

对于Question(题目)和 ExamQuestion(试卷试题)
一般想到是 继承 关系, 就是 ExamQuestion extends Question,我也以继承关系实现了一个模型,组卷的时候,根据一个题目Question产生一个ExamQuestion对象。 由于我的继承关系通过hibernate实现,是放到同一个表保存继承关系。结果ExamQuestion和Question放在同一张表。

这看起来不是很好,因为“题库管理”是考试系统的核心,里面的题目应该与已经组卷的题目分开表存放。

因此我在模型中加入一个新的对象,PaperQuestion (试卷题目)来存放 Question的复制, 然后ExamQuestion在关联到PaperQuestion. 这样一个ExamQuestion(试卷试题)的组成如下:

examQuestionId:String (ID)
perQuestionGrade:String (题目分数)
paperQuestion:PaperQuestion(题目内容)

至于PaperGroupTemplete 和 ExamPaper

个人觉得本质上两者也可以没任何关系, ExamPaper也可以不包含PaperGroupTemplete 的信息,因为可以通过遍历ExamPaper的所有ExamQuestion计算试卷的题型,数目,分数等信息。
目前我的模型是两者没任何信息

谢谢freebox和dearshor ,经过这几天的修改,模型已经初步成型,和hibernate相关的处理也搞明白了,积累很多oo设计方面经验,虽然模型还不算完美,但至少思路都理顺了。

象question 和 examQuestion 的关系处理还可能需要进一步修改。。。