Implementing many-to-many relationships
-
Hi all -
Not really a Qt question, but...I'm working on an app that several object classes, such as:
- location
- equipment
- activity
- probably a couple others
There will be multiple instances of each class, and a many-to-many relationship between them. I'm trying to decide how best to implement these relationships. There seems to be two basic choices:
-
Each object will maintain a list of associated items: for example, a location object will have lists that point to equipment object identifiers, activity object identifiers, etc. and so on. Other objects would have similar lists. This approach will have some redundancy, but it won't be terrible, as an installation will likely have fewer than 100 instances of any class. Updating the relationship, of course, will have to be done in two places, and while again the overhead won't be bad, there's greater risk of "missing something."
-
maintain a few separate lists that enumerate the relationships. This approach would simplify the contents of the classes I mentioned, but at the expense of creating a new class (the list itself).
There are doubtless other approaches, but these are the 2 that come to mind. Currently, I'm leaning towards the latter.
I'm not looking for a definitive answer (just thinking out loud), but...what does anyone think about the respective merits of either approach?
Thanks for any ideas...
-
Hi,
Simpler is cleaner, go with the latter !
-
@tilz0R an intermediate table is probably the better choice. I don't think, though, that I want to use pointers. I think it would be getter to give each item a unique identifier, and use that identifier in the table. Given how small my lists will be, there's really not much overhead searching through them for the desired items.
-
@mzimmers maybe a QMultiHash would be an idea.
-
So, to further this a bit, let's say I create a table that contains all the associations between activities and equipment. When I create my activity and equipment objects (which contain lists themselves), I'll pass them a pointer to this table. My activity and equipment objects must contain functions to retrieve information on a list entry based on the UUID value retrieved from the table.
Of course, this means that each class (Activity, Equipment, etc.) must have knowledge of the other classes, in order to access those retrieval functions. This could make for some cruddy code.
I realize that such an approach could get unwieldy if I had many such classes and relationships, but in this application it's only going to be a handful. Does this seem like a reasonable way to go? Is there a preferred method of doing this?
Thanks...
-
Do these object have a common interface ?
Are they QObject based ? -
@SGaist said in Implementing many-to-many relationships:
Do these object have a common interface ?
Not sure what you mean by this.
Are they QObject based ?
The lists are, but the items contained by a list are not (because basing them off of QObject would lose the copy c'tor and the assignment operator, and I need to copy these [I think]).
-
@mzimmers said in Implementing many-to-many relationships:
@SGaist said in Implementing many-to-many relationships:
Do these object have a common interface ?
Not sure what you mean by this.
I think what he is asking is whether you can create a common abstract or pure virtual class that defines a common set of actions shared by the various concrete object...or multiple interface bases.
-
@Kent-Dorfman said in Implementing many-to-many relationships:
I think what he is asking is whether you can create a common abstract or pure virtual class that defines a common set of actions shared by the various concrete object...or multiple interface bases.
So, base my various list classes from a common class? I could certainly do that. I'm not sure how this is going to move the needle, though.
-
@mzimmers
Since this is "Brainstorm" I can just make an unhelpful observation, right? Everyone here says do the relationships with external maps/tables. This is indeed how relational databases do it, but it seems to me that's completely non-OO, which is a shame in your C++ code. Objects having their own lists of other related objects may be "trickier" to ensure code gets it right but to me fits more neatly into a nice OO paradigm.Just saying :)
-
@mzimmers said in Implementing many-to-many relationships:
There will be multiple instances of each class, and a many-to-many relationship between them. I'm trying to decide how best to implement these relationships. There seems to be two basic choices
You're asking it backwards. Before deciding the choices, you have to actually clarify what sort of relationships we are talking about. The chosen word is a bad one, as it's a catch all for many different things. Is there hierarchical structure to them, or not (this'd imply ownership)? Perhaps those objects are peers, or composed in some other way? From the names I'd say it's a mixture of owning and peer-like relationships, or perhaps some objects are self-owning?
More info needed. -
@kshegunov maybe this will help describe what I'm trying to do:
The controller (the hardware that hosts the application) supports a site. Within a site, there may be one or more zones (referred to as "locations" above), equipment items, and activities.
One zone may use several equipment items and several activities. A given piece of equipment may support multiple zones and activities. And an activity may entail multiple zones.
Based on the preceding, I don't think there is any "ownership" in the UML sense of the word.
@JonB your suggestion was my original idea. But, you're right about the potential trickiness of implementation. As @SGaist pointed out, using an external table does seem "cleaner."
-
From your explanation it sounds to me that the site owns everything, and also may be needed to dispatch between the different components. As far as using goes, depending on exactly what you put in this word a publish-subscribe sort of architecture that is managed by the site may be reasonable and clean.
Personally, I don't think breaking encapsulation and coupling everything through some global map is a good idea. In a good architecture you couple things together only when you really need to.
For example the zone may own the equipment items and activities, or perhaps the site could initialize them and inject to the zone references to the relevant object(s). Similar considerations for a piece of equipment, does it really need to know what zone and what activity is using it to be able to function? If not then it absolutely doesn't depend on them and/or the dependency if such materializes can be injected from the owner (e.g. the site) whenever the need arises.EDIT: If you're wondering what I mean by injecting the dependency in this context: https://en.wikipedia.org/wiki/Dependency_injection
-
@kshegunov said in Implementing many-to-many relationships:
For example the zone may own the equipment items and activities
A zone will certainly use equipment and activities, but both of those could well be used in other zones. This where the notion of "ownership" becomes tricky.
Similar considerations for a piece of equipment, does it really need to know what zone and what activity is using it to be able to function?
Well...I'm not sure. If the user creates a schedule entry (an activity) that uses equipment A from 10 AM to noon, then later tries to create another entry from 11 AM to 1PM with the same equipment, this needs to fail. It would be cleaner if it fails at equipment selection time, so he can say "oh, OK, I'll use equipment B for this instead." Without keeping a list of activities within the equipment, I'll have to traverse all the activities in all the zones to determine this. Probably not a huge deal from a CPU standpoint, but could make for lousy code.
@kshegunov said in Implementing many-to-many relationships:
If not then it absolutely doesn't depend on them and/or the dependency if such materializes can be injected from the owner (e.g. the site) whenever the need arises.
EDIT: If you're wondering what I mean by injecting the dependency in this context: https://en.wikipedia.org/wiki/Dependency_injectionThanks for the reference. As it turns out, I've used the assembly form of injection in the past. It seems viable for this application, provided that the number of things that require mutual awareness stays low. These lists of course will be dynamic -- when a site is initialized, all lists will be empty until equipment discovery occurs, activities are created, etc.
-
@mzimmers said in Implementing many-to-many relationships:
Well...I'm not sure. If the user creates a schedule entry (an activity) that uses equipment A from 10 AM to noon, then later tries to create another entry from 11 AM to 1PM with the same equipment, this needs to fail. It would be cleaner if it fails at equipment selection time, so he can say "oh, OK, I'll use equipment B for this instead." Without keeping a list of activities within the equipment, I'll have to traverse all the activities in all the zones to determine this. Probably not a huge deal from a CPU standpoint, but could make for lousy code.
I don't follow. The equipment can keep the timeline for when it is used by anybody who reserved it. This doesn't require it to know what activities or zones or w/e it is used in. It just needs to know that somebody is going to use it at some point. You can immediately return an error if you try to reserve some equipment for some time, but it's reserved. E.g. (pseudocode)
class Equipment { public: bool reserve(const TimeSlot &); void free(const TimeSlot &); bool isFree(const TimeSlot &) const; private: QVector<TimeSlot> reserved; };
Then what the activity needs to keep is a list of time slots + equipment references.
class Activity { public: Activity(const QVector<Equipment *> &items); bool select(Equipment *, const TimeSlot &) { // ... check if the equipment is available, insert in timeTable, etc. } QHash<TimeSlot, Equipment *> timeTable; };
@mzimmers said in Implementing many-to-many relationships:
I'll have to traverse all the activities in all the zones to determine this.
Why? Is that the activity keeps track of when an equipment it used? Or rather I should paraphrase - should an activity manage the internal state of a piece of equipment? You could make an argument that the infotainment system in a car could control the fuel injection cycle, but then again, should it?
-
@kshegunov said in Implementing many-to-many relationships:
make an argument that the infotainment system in a car could control the fuel injection cycle, but then again, should it?
Sometimes I think mine does in my car....