核心数据模型设计——改变“活”对象也会更改保存的对象
我正在开发我的第一个 Core Data 项目(在 iPhone 上)并且我真的很喜欢它。核心数据是很酷的东西。
然而,我遇到了一个设计困难,我不确定如何解决,尽管我认为这是一个相当常见的情况。它涉及数据模型。
为了清楚起见,我将使用一个想象的足球比赛应用程序作为示例来说明我的问题。假设有 NSMO,称为 Downs 和 Plays。播放功能类似于唐斯使用的模板。用户创建 Play(例如 Bootleg、Button Hook、Slant Route、Sweep 等)并填写各种属性。戏剧与唐斯有一对多的关系。对于每个 Down,用户决定使用哪个 Play。当执行 Down 时,它使用 Play 作为其模板。每次运行后,都会将其存储在历史记录中。该程序会记住所有打过的唐斯比赛。
到目前为止,一切都很好。这一切工作正常。
我的问题涉及当用户想要更改 Play 的详细信息时会发生什么。假设它最初涉及向左传球,但用户现在希望它是向右传球。然而,进行该更改不仅会影响该 Play 的所有未来执行,还会更改历史记录中存储的 Play 的详细信息。实际上,唐斯的记录被“污染”了,因为播放模板已被更改。
我一直在研究针对这种情况的几种可能的解决方案,但我想 SO 的天才们比我更了解如何处理这个问题。尽管如此,我提出的潜在修复措施是:
Play 的“版本控制”。对 Play 模板的每次更改实际上都会创建一个具有相同名称的新的、单独的 Play 对象(据用户所知)。然而,在幕后,它实际上是一个不同的游戏。这会起作用,AFAICT,但似乎它可能会导致 Play 对象的疯狂扩散,尤其是。如果用户不断在同一个 Play 的多个版本之间来回切换(每次用户切换时都会创建一个又一个对象)。是的,该应用程序可以检查预先存在的、相同的 Plays,但是......它看起来就像一团糟。
Have Downs 在保存时记录他们使用的 Play 的详细信息,但不作为 Play 对象。这看起来很荒谬,因为 Play 对象在那里只保存那些细节。
认识到 Play 对象实际上实现了 2 个功能:一是作为 Down 的模板,二是记录使用的模板。这两个函数与 Down 有不同的关系。第一个(模板)具有一对多关系。但第二个(记录)具有一对一的关系。这意味着创建第二个对象,例如“Play-Template”,它将保留与 Downs 的一对多关系。 Play 对象将被重新配置为与 Downs 具有一对一关系。 Down 将使用 Play-Template 对象来执行,但使用新类型的 Play 对象来存储所使用的模板。正是这种从一对多关系到一对一关系的转变才是问题的症结所在。
即使把这个问题写出来也帮助我变得更清楚。我认为解决方案 3 之类的东西就是答案。然而,如果有人有更好的想法,或者只是确认我走在正确的轨道上,那将会很有帮助。 (请记住,我并不是真的在制作足球游戏,只是使用每个人都能理解的比喻更快/更容易。)
I'm working on my first Core Data project (on iPhone) and am really liking it. Core Data is cool stuff.
I am, however, running into a design difficulty that I'm not sure how to solve, although I imagine it's a fairly common situation. It concerns the data model.
For the sake of clarity, I'll use an imaginary football game app as an example to illustrate my question. Say that there are NSMO's called Downs and Plays. Plays function like templates to be used by Downs. The user creates Plays (for example, Bootleg, Button Hook, Slant Route, Sweep, etc.) and fills in the various properties. Plays have a to-many relationship with Downs. For each Down, the user decides which Play to use. When the Down is executed, it uses the Play as its template. After each down is run, it is stored in history. The program remembers all the Downs ever played.
So far, so good. This is all working fine.
The question I have concerns what happens when the user wants to change the details of a Play. Let's say it originally involved a pass to the left, but the user now wants it to be a pass to the right. Making that change, however, not only affects all the future executions of that Play, but also changes the details of the Plays stored in history. The record of Downs gets "polluted," in effect, because the Play template has been changed.
I have been rolling around several possible fixes to this situation, but I imagine the geniuses of SO know much more about how to handle this than I do. Still, the potential fixes I've come up with are:
"Versioning" of Plays. Each change to a Play template actually creates a new, separate Play object with the same name (as far as the user can tell). Underneath the hood, however, it is actually a different Play. This would work, AFAICT, but seems like it could potentially lead to a wild proliferation of Play objects, esp. if the user keeps switching back and forth between several versions of the same Play (creating object after object each time the user switches). Yes, the app could check for pre-existing, identical Plays, but... it just seems like a mess.
Have Downs, upon saving, record the details of the Play they used, but not as a Play object. This just seems ridiculous, given that the Play object is there to hold those just those details.
Recognize that Play objects are actually fulfilling 2 functions: one to be a template for a Down, and the other to record what template was used. These 2 functions have a different relationship with a Down. The first (template) has a to-many relationship. But the second (record) has a one-to-one relationship. This would mean creating a second object, something like "Play-Template" which would retain the to-many relationship with Downs. Play objects would get reconfigured to have a one-to-one relationship with Downs. A Down would use a Play-Template object for execution, but use the new kind of Play object to store what template was used. It is this change from a to-many relationship to a one-to-one relationship that represents the crux of the problem.
Even writing this question out has helped me get clearer. I think something like solution 3 is the answer. However if anyone has a better idea or even just a confirmation that I'm on the right track, that would be helpful. (Remember, I'm not really making a football game, it's just faster/easier to use a metaphor everyone understands.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为你需要重新开始你的设计。
(1) 为什么使用PlayEntity作为DownEntity的模板?实体实际上是(在底层)类,因此类定义本身就是每个实例的“模板”。
(2) 管理对象应代表真实对象或真实信息关系的数据模型。因此,您需要认真思考您想要建模的真实对象或信息是什么。一个好的起点是问问自己如何用笔和纸记录这些信息。
在你的例子中,上场和下场模拟了完全不同的东西。
Down 是按时间顺序发生的事件。在任何特定的游戏中,只有一个特定的“Down”。这意味着足球历史上每场比赛中的每场比赛都是独一无二的。因此,Down 数据模型实体主要感兴趣的是对Down 与其他Down 以及整个游戏的时间关系进行建模。
相比之下,戏剧是一个空间事件。比赛并不是唯一的,并且经常在一场比赛中以及在一场比赛之间重复出现。 Play 实体应该关注球员、球和场地之间的空间关系。
您最终会得到这样的结果:
请注意,两个实体都不会复制另一个实体的属性中保存的信息。它们也不共享继承,因为它们没有对游戏的相同方面进行建模。 Down 模拟时间序列,Play 模拟空间序列。这需要他们两个都完整地描述每一次下来所发生的事情。
您可以通过首先创建您想要的任何标准化 PlayEntities 来构建数据库。如果您有一部小说,您将创建一个新的 PlayEntity 并根据需要填充它。每次您有一个 Down 时,您都会创建一个 DownEntity 并创建与现有或新创建的 PlayEntity 的关系。
I think you need to start over with your design.
(1) Why are using a PlayEntity as a template for the DownEntity? Entities are really (under the hood) classes so the class definition itself is the "template" for each instance.
(2) Managed Objects should represent data models of real objects or real information relationships. Therefore you need to think hard about what you the real objects or information you are trying to model. A good place to start is to ask yourself how this information would be recorded with pen and paper.
In your example, Plays and Downs model entirely different things.
A Down is an event ordered in time. There is only one particular Down in any particular game. This means every Down every played in every game in the history of football is utterly unique. A Down data model entity therefore would be primarily interested in modeling the Down's relation in time to other downs and the total game.
A Play by contrast is a spatial event. Plays are not unique and are often repeated within a game and from game to game. A Play entity should be concerned with the spatial relationships between players, the ball and the field.
You would end up with something like this:
Note that neither entity duplicates information held in the attributes of the other. Neither do they share inheritance because they don't model the same aspects of the game. The Down models a temporal sequence and the Play models a spatial one. It requires both of them to completely describe what happened upon each down.
You would build your database by first creating any standardized PlayEntities you wanted. If you had a novel play, you would create a new PlayEntity and populate it as needed. Every time you had a down you would create a DownEntity and create a relationship to an existing or newly created PlayEntity.
我会选择你的#3 作为最清晰、最明智的选择。它很好地涵盖了(隐喻的)应用程序的预期用途。
不久前,我在使用一款跟踪人们测试的应用程序时遇到了类似的情况。测试包含多个问题,并且可以在多个日期进行更改和重新进行。拥有测试模板以及单独的测试对象,使整个模型更容易处理。
I would go with your #3 as the clearest, most sensible option. It covers the intended usage of your (metaphorical) application well.
I ran into a similar situation a while back with an app that tracked tests administered to people; the tests contained multiple questions, and could be changed and re-administered on multiple dates. Having a test template, along with individual test objects, made the entire model much easier to deal with.