核心数据模型设计 - 更改“实时”对象也会更改已保存的对象

时间:2010-03-29 19:39:34

标签: ios iphone core-data data-modeling

我正在开发我的第一个Core Data项目(在iPhone上),我真的很喜欢它。核心数据很酷。

但是,我遇到了一个设计难题,我不确定如何解决,尽管我认为这是一个相当常见的情况。它涉及数据模型。

为了清楚起见,我将以假想的足球游戏应用程序为例来说明我的问题。假设有NSMO称为唐斯和戏剧。播放功能类似于Downs使用的模板。用户创建Plays(例如,Bootleg,Button Hook,Slant Route,Sweep等)并填写各种属性。戏剧与唐斯有很多关系。对于每个Down,用户决定使用哪个Play。执行Down时,它使用Play作为其模板。每次运行后,它都会存储在历史记录中。该节目记得有史以来所有的唐斯。

到目前为止,这么好。这一切都很好。

我遇到的问题是当用户想要更改Play的详细信息时会发生什么。假设它最初涉及向左传递,但是用户现在希望它是向右传递。但是,进行此更改不仅会影响该Play的所有未来执行,还会更改存储在历史记录中的播放的详细信息。由于Play模板已被更改,Downs的记录实际上已被“污染”。

我一直在解决这种情况的几种可能的修复,但我想SO的天才知道如何处理这个比我更多。不过,我提出的潜在修复方法是:

  1. 播放的“版本控制”。对Play模板的每次更改实际上都会创建一个具有相同名称的新的单独Play对象(就用户而言)。然而,在引擎盖下,它实际上是一个不同的游戏。这可行,AFAICT,但似乎它可能会导致Play对象的疯狂扩散,尤其是。如果用户在同一个Play的多个版本之间来回切换(每次用户切换时在对象之后创建对象)。是的,该应用程序可以检查预先存在的,相同的播放,但......它只是看起来像一团糟。

  2. 让Downs在保存时记录他们使用的Play的详细信息,但不记录Play对象。这看起来很荒谬,因为Play对象是那里来保存那些细节。

  3. 认识到Play对象实际上正在实现两个功能:一个是Down的模板,另一个是记录使用的模板。这两个函数与Down有不同的关系。第一个(模板)具有多对多关系。但第二个(记录)有一对一的关系。这意味着要创建第二个对象,例如“Play-Template”,它将保留与Downs的多对多关系。播放对象将被重新配置为与Downs具有一对一关系。 Down将使用Play-Template对象执行,但使用新类型的Play对象来存储使用的模板。正是这种从一对多的关系变为一对一的关系,代表了问题的关键。

  4. 即使写出这个问题,也能让我更清楚。我觉得像解决方案3这样的东西就是答案。但是,如果有人有更好的想法,或者甚至只是确认我正在走上正轨,那将会有所帮助。 (记住,我不是在制作足球比赛,只是更快/更容易使用每个人都能理解的比喻。)

2 个答案:

答案 0 :(得分:1)

我认为你需要重新开始设计。

(1)为什么使用PlayEntity作为DownEntity的模板?实体实际上是(在引擎盖下)类,因此类定义本身就是每个实例的“模板”。

(2)托管对象应代表真实对象或真实信息关系的数据模型。因此,您需要认真思考您尝试建模的真实对象或信息。一个好的开始就是问问自己如何用笔和纸记录这些信息。

在你的例子中,Plays and Downs模型完全不同。

Down是及时订购的事件。在任何特定的游戏中只有一个特定的Down。这意味着足球历史上每场比赛中的每一场比赛都是完全独一无二的。因此,向下数据模型实体主要感兴趣的是将Down的关系及时建模到其他向下和整个游戏。

相比之下,Play是一个空间事件。比赛不是独一无二的,经常在比赛中以及从比赛到比赛中重复。 Play实体应关注球员,球和场地之间的空间关系。

你最终会得到这样的东西:

DownEntity{
    game;
    half;
    quarter;
    turnover;
    gameClockTime;
    yardLine;
    penalties;
    play --(required,Cascade)->PlayEntity.down
    previousDown --(optional, nullify)-->Down.nextDown;
    nextDown --(optional, nullify)-->Down.previousDown
}


PlayEntity {
    playName;
    //whatever other detail you want to model
    down --(optional,nullify)-->>DownEnity.play;
}

请注意,两个实体都不会复制另一个实体的属性中保存的信息。他们也没有共享继承,因为他们没有模仿游戏的相同方面。 Down模拟时间序列,Play模拟空间序列。它要求两者完全描述每次下来发生的事情。

您首先要创建所需的标准化PlayEntities来构建数据库。如果您有一个新颖的游戏,您将创建一个新的PlayEntity并根据需要填充它。每次你失败都会创建一个DownEntity并创建一个与现有或新创建的PlayEntity的关系。

答案 1 :(得分:0)

我会选择你的#3作为最明智,最明智的选择。它很好地涵盖了您(隐喻)应用程序的预期用途。

我在一段时间内遇到了类似的情况,其应用程序跟踪了对人们进行的测试;测试包含多个问题,可以在多个日期进行更改和重新管理。拥有测试模板以及各个测试对象,使整个模型更容易处理。