views:

162

answers:

2

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:

1) "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.

2) 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.

3) 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.)

Thanks.

A: 

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.

Tim
+1  A: 

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:

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;
}

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.

TechZen
Perhaps my description of my current design is not detailed enough, and has lead to misunderstanding. What you are describing is all already in my design. Even the standardized "PlayEntities" that it starts out with. Your mock Down and Play entities look essentially like the ones I already have.
mwt
The problem with the design you have here is the same as the one I have outlined above. If you change the details a PlayEntity, it changes the details of (the Play Entity) in all the saved Downs, thereby wrecking them as actual histories of what happened. I.e. the PlayEntity that it says was used is now actually slightly different than the one that was used.
mwt
If each play can be different, then it would be best that the model reflect that by creating a different PlayEntity for each Down. There is no compelling reason to reuse PlayEntities unless they are in fact exactly duplicated within the limits of the model. You could have template data stored elsewhere to create Plays that are similar and/or ou could simply name plays that use the same basic form the same so you could do a search on all Downs whose play.name == "Right42. However, each actual instance of a PlayEntity would be unique even if its attributes are identical to another.
TechZen
I believe something like this is what I'm suggesting in the #3 part of my question: having Play-Templates as well as Plays (or, as you would have it Play-TemplateEntities and PlayEntities). Play-Templates would be reusable, but Plays would be unique. I like your idea about the name search, as it helps to solve one of the downsides of the #3 solution (if Plays are unique, it makes it hard to fetch something like "all downs that use Play X").
mwt
Yes, you should break up the entities to reflect different parts of the reality you are modeling. Since template plays and the actual play of each actual down are entirely separate real world objects (the first is in a book, the second happens on the field) they should each have a separate entity. One of the biggest mistakes people make when starting out in Core Data is trying to cram as much functionality into every entity as possible. Instead, you should do the opposite, breaking each part of the model up into smallest possible entities.
TechZen