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.
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....
-
@kshegunov said in Implementing many-to-many relationships:
the infotainment system in a car could control the fuel injection cycle, but then again, should it?
I believe, it would be indeed very entertaining, if it does ;-)
-
@kshegunov 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?
Imagine an installer who is creating a new activity. One of the first steps is to associate equipment with that activity. If the equipment has no knowledge of what other activities its already committed to, and therefore no knowledge of what its committed schedule is, each time the installer selects a new equipment item, the system will have to search all the activities, comparing their schedules and equipment lists to see whether there are conflicts with the new activity being created.
To use your example, the infotainment system doesn't control the fuel injection, but if something were to go wrong in the fuel injection system, the infotainment system might want to stop blasting "Stairway to Heaven" and alert the driver of the malfunction.
-
I would personally advice against using pointers-to-object. Its slower and more important, its more fragile.
It is more fragile because when you delete an object, all the pointers to it need to manually be updated / removed, and if you fail to do so you'll have crashes if you are lucky.You could go with weak-pointers, but that sounds like its just adding complexity and not solving the issue.
My suggestion is this;
Have one place that owns all your "stuff".
Likely one named list for each type of "stuff". So your have a class that has a list (in a map) of equipment, another list of activities etc.
This is in the form of aQMap<int, Equipment> e;
.Linking between those items then is using an integer. The integer you have as the key in your hash.
This means you need to do an extra step (a search in the map) to refer to anything, but its much more safe. Additionally, you can change your Equipment object to now not be 'new'-ed, but just a simple struct. Of which some members will be integers pointing to other maps of 'stuff'.
Its not very object oriented as one object would own all of them and would update and resolve the links between them. But its quite a bit easier to write and maintain.
postscript; if you must, you can make all of this the private content of a 'manager' class and provide some helper classes on top to make it look all object-oriented again. (example: manager, pretty-object, implementation).
-
@Pl45m4 said in Implementing many-to-many relationships:
I believe, it would be indeed very entertaining, if it does ;-)
Well, I get the sense that @mzimmers is eventually going to tell us if it is entertaining or not.
@TomZ said in Implementing many-to-many relationships:
This means you need to do an extra step (a search in the map) to refer to anything, but its much more safe.
That's a complete misnomer. How is this safer:
QMap<QString, Something> map; int foo = map.find("blah").value().doStuff(); // And of course if there's no "blah" that crashes just as well, so there's nothing intrinsically safer in a map, or a hash, or w/e
Cognate with:
class Equpment : public QObject { ... }; class Activity { QHash<TimeSlot, QPointer<Equipment>> timetable; };
Same consideration - you must check if what you're referring to actually exists.
-
@kshegunov said in Implementing many-to-many relationships:
How is this safer
Sure you can use weak pointers (or qpointers, or shared pointers), but that just uses more cycles to zero the pointers.
As I wrote in the original post:
You could go with weak-pointers, but that sounds like its just adding complexity and not solving the issue.
There is no difference between checking the null pointer and you checking the iterator be 'end()'. It doesn't solve the problem.
But the structs only solution itself is much easier to handle, much cheaper on resources (great for embedded) and generally easier to think about.
-
@TomZ said in Implementing many-to-many relationships:
Sure you can use weak pointers (or qpointers, or shared pointers), but that just uses more cycles to zero the pointers.
Cycles pretty much irrelevant in business code, I much rather rely on the prefetcher being smart enough to build its heuristic (which it really is!). The only place it truly matters is whenever you're low-level optimizing a hot codepath, like in a heavy calculation. I'd take the additional indirection any day if I can maintain the code more easily in the long-run, which is the typical case for business logic (which appears to be the case here).
-
@kshegunov said in Implementing many-to-many relationships:
Cycles pretty much irrelevant in business code
you can waste your cycles, for sure.
As I wrote, it doesn't actually solve the bigger issue, it only solves the problem you introduce when you use pointers. Don't use pointers and the problem doesn't need solving.
So my suggestion is to not use pointers.
-
@TomZ said in Implementing many-to-many relationships:
Don't use pointers and the problem doesn't need solving.
You can't not use pointers, and I imagine you know that. Neither in C, nor in C++, nor in assembly. Every heap allocation is a pointer, and even if you don't keep anything in the heap, which is dubious to begin with (with one notable exception), there are other pointers out there like C-like arrays, function pointers, lvalue references, etc.