I have an event class that holds details of a volunteering “Event” class. There is an “EXCO” Class that can create and edit details of said Events. There is also a “Volunteer user” class is allowed to register for such events.
My initial approach to this was to have a “Event DataBase” Class that holds an array of Events, and provides interfaces for the “EXCO” class to use to Create events, and delete events. This way, the “EXCO” class only interacts with the “Events” class via interfaces, which I hope is a good use of the DIP.
However, I have difficulty doing the same for the “Volunteer” and “Event” class. “Volunteer” can view and sign up for “Events”. Hence I’m under the impression that the “Volunteer” class should CONTAIN a list of “Events”.
Likewise, an “EXCO” should be able to check an “Event” and see which “Volunteers” have signed up. I’m also thinking of putting an array of “Volunteer” into “Event” class.
However, since these classes literally contain each other, I feel like there is a missing layer of abstraction between them, and it feels like a bad design since any changes to either class would affect the other.
Is my understanding of a good DP flawed or is there a better way to do this? Thanks!
Volunteers and Events don’t contain but reference each other. Each can exist on its own.
In the real world, volunteers would have some kind of list of events they will participate in, most likely as entries of their calendar where they are mixed with dentist appointments and birthday reminders. Events (or rather their coordinators) have lists of participants.
You can implement a similar scheme within your application, but just as in the real world there is a risk that these lists diverge somehow.
An easier solution in the application would be to have a global register of event participation, which can be updated and queried by your EXCO class (and could be queried by volunteers and events to retrieve their event or participant lists). Of course such a global register can’t exist in the real world.Sounds too technical/relational? Well it is, but software solutions to real world problems are not required to model the real world faithfully.
The principle I’d be most concerned with here is called:
Single Source of Truth
Maintaining a consistent bidirectional relationship is not something you want to be doing manually. Do that through some abstraction that maintains it for you.
I feel like throwing hash sets at this is a premature optimization. Let’s do something dead simple.
Register is a
Commitment was an object that had “Volunteer” and “Event” fields?
Now you have a single source of truth. You can add and delete
Commitments in one place atomically. Once you show that looking things up in this is too slow, only then add hash tables that are built from this dead simple list of commitments we call the register. The hashes take either a volunteer or an event and return a list of commitments that hold both.
Done this way the possibly inconsistent hashes are not your source of truth. Just an optimization. The truth still lies with the list of commitments.
Sound complicated? Well you were going to need lists in the hashes anyway. If building this is slow then build it as you go.
And finally you can always break down and make the DB do this in a table with actual indexes.
The real trick is designing the interface so that the outside neither knows nor cares which implementation you’re using. Make it work with all of them and your using code can go untouched when you change how it works.