我有一个不容易解释的场景,但我确信这是一个非常常见的问题。我会尽力说明问题。
假设我们有一个调查应用程序,它允许我们创建调查。每项调查都有自己的结构(包含问题,问题分组和排序等)。这些调查由管理员管理,并称为主调查模板。 现在,用户可以选择其中一个主调查,进行一些自定义并对某些人进行调查。
因此,基本上我们的调查都有相同的结构(集合,属性等),但数据可能不同。
您如何为数据库建模?
我的想法是将所有内容存储在一个表中,并创建一个将模板与传统模板分开的列。
tbl_Survey (id, name, conducted_on)
您如何为课程建模?
我的想法是:
Survey {
Name
Questions
}
ConductedSurvey : Survey {
//gets the master according to the name
GetMaster()
}
答案 0 :(得分:4)
所以,基本上我们的调查都有相同的结构(集合,属性等),但数据可能不同......我的想法是将所有内容存储在一个表中并创建一个分隔模板的列进行的。
好的,你所说的是一个原型。 “模板”调查是一个原型。显然,如果原型与基于原型的实例具有完全相同的结构,那么为同一结构创建完全不同的表将是愚蠢和浪费的 - 当您意识到这意味着该结构的任何变化时更是如此必须反映两组。
我是否会添加一列来区分原型/模板和进行的调查?不,可能不是。相反,我会添加另一个表,与根调查表具有一对一的关系。在这个表中,我添加了一些区分原型和非原型的元数据。
有三个原因:1)在任何合理的系统中,原型只占总调查的一小部分。 2)我经常想要列出所有原型,例如:一个“创建新的调查向导”,列出了基于新调查的原型选择。并且3)保留那些额外的数据:
create table survey_prototype (
id int not null primary key,
survey_id references survey(id) -- the regular survey table
wizard_description varchar(80)
. . . .
);
现在,我认为该调查也有描述,但对于原型而言,该描述类似于“替换我这是用户将看到的描述”,但是wizard_description类似于“原型政治民意调查”。
现在,由于原型/模板的任何查找都没有机会返回进行的调查(因为调查连接到survey_prototype),你的getMaster看起来(概念上,大概是你使用ORM),如下所示:
ConductedSurvey : Survey {
//gets the master according to the name
GetMaster() { "select * from survey_prototype join survey..."
}
重要提示:调查与其他课程有很多关系。如果我们决定继承。是否所有这些都是子类(因为我们会为每个对象复制master的数据)?
你是对的:对于你通过ORM检索的任何原型,你必须深度复制它以保存新的调查,而不是覆盖原型。因为你无论如何都必须进行深层复制,所以在深层复制中你可以代替原型使用的基类,制作一个子类副本。
当然,您必须在层次结构的每个级别做出决定;在一个类中封装深度复制转换的每个转换策略会很好。访问者模式将执行此操作,因为它为您的类型的每个(基础)类设置了一个重载visit
函数:所以(至少)visitSurvey
,visitQuestion
,visitAnswer
。< / p>
由于您将处理一棵树(根据调查,带有子问题和孙子答案,即复合模式),我建议您使用复制/转换器中的访客模式。由于您的课程相对稳定,访问者将很好地工作。它允许您拥有多个不同的具体访问者,每种类型的转换一次(当需要显示或评分调查时,您也可以为此编写访问者 - 因此您几乎可以获得该功能)免费“一旦你设置了访客模式。”
要处理数据库中的子类,可以使用任何一种常见的nhibernate模式;这样,一旦你访问并转换了,你就会有一个新的非原型调查树,可以通过nhibernate自动保存到数据库中。
总结一下:survey_prototype表,得到一个原型,当你在survey_prototype上请求root时,nhibernate会检索整个树,用深度复制变换器访问那棵树,返回复制的root,让用户写一下,保存复制的root并让nhibernate以递归方式保存树的每个节点。当用户需要查看非原型调查时,使用nhibernate从调查中拉出根,使用显示访问者显示它等。