邀请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前辈所说,就用<subclass />

关于上述类图,如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, 那使用 <subclass />,也不知道如何使用。

这几天研究了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 的关系处理还可能需要进一步修改。。。