1

我有一个不容易解释的场景,但我确信这是一个非常普遍的问题。我会尽力说明问题。

假设我们有一个调查应用程序,它允许我们创建调查。每个调查都有自己的结构(包含问题、问题被分组和排序等)。这些调查由管理员管理并称为主调查模板。现在,用户可以选择其中一项主调查,进行一些自定义并对某些人进行调查。

所以,基本上我们的调查都具有相同的结构(集合、属性等),但数据可能不同。

你如何为数据库建模?

我的想法是将所有内容存储在一个表中并创建一个将模板与执行模板分开的列。

tbl_Survey (id, name, conducted_on)

你如何为你的课程建模?

我的想法是:

Survey {
  Name
  Questions
}

ConductedSurvey : Survey {
  //gets the master according to the name
  GetMaster()
}

重要提示:调查与其他类有很多关系。如果我们决定子类化。是否应该将它们全部子类化(因为我们将从 master 复制每个对象的数据)?

4

1 回答 1

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、、、visitQuestionvisitAnswer

由于您将处理一棵树(植根于调查,有子问题和孙子答案,即复合模式),我建议您在副本/转换器中使用访问者模式。由于您的课程相对稳定,因此访问者会很好地工作。它允许您拥有多个不同的具体访问者,每种类型的转换都有一个(当需要显示或评分调查时,您也可以为此编写访问者 - 因此您将获得该功能几乎“用于免费”设置访客模式后)。

要处理数据库中的子类化,您可以为此使用任何一种常见的 nhibernate 模式;这样,一旦您访问并转换,您将拥有一个新的非原型调查树,它可以由 nhibernate 自动保存到数据库中。

总结一下:survey_prototype 表,得到一个原型,当你在survey_prototype 请求根时,nhibernate 将检索整个树,使用返回复制根的深度复制转换器访问该树,让用户在上面写,保存复制根并让 nhibernate 递归地保存树的每个节点。当用户需要查看非原型调查时,使用 nhibernate 从调查中拉根,使用显示访问者显示等。

于 2009-05-04T08:45:27.110 回答