What’s the Verdict?

Steven Kandow
5 min readMay 20, 2020

--

Can Many-to-Many-to-Many Relationships Work with Ruby Object Orientation?

When I was younger, I loved playing chess. Everything about the game, from the unique move sets, to thinking ahead multiple moves in advance, to the various possible outcomes of the game intrigued me. A few years ago, I came across a 4-person chess game and knew I had to play it. If you’re a chess fan and want to try and wrap your mind around a new way of thinking about the game, I highly encourage it.

I was reminded of this throughout the past week as I began to learn about Ruby Object Orientation, particularly regarding Many-to-Many relationships. I began to hypothesize: if we can join two classes of many instances using a joiner class, could we do the same with three?

This question recently collided with my wrestling with the end of another of my favorite courtroom dramas: How to Get Away with Murder. Over time, I’ve found myself drawn to the drama of the courtroom with shows like Law and Order, books like To Kill a Mockingbird, and films like A Few Good Men ranking as some of my favorites. Even daytime court TV shows like Judge Judy and The People’s Court can be fun distractions at times (though I don’t find them nearly as compelling as a good drama…)

I recently began to imagine what a world would look like in which all my favorite attorneys collided with Daytime Court Room TV show judges. We’d have many great prosecuting attorneys like Law and Order’s Jack McCoy, or great defense attorneys like To Kill a Mockingbird’s Atticus Finch arguing their cases in front of (sometimes overly dramatic) judges like Judy Sheindlin (better known simply as “Judge Judy”).

How could we create these hypothetical cases featuring these judges, prosecutors, and defense attorneys? We have many prosecuting attorneys, and many defense attorneys, and we’d have many judges. So we have three “many” objects:

  • Judges
  • Prosecutors
  • Defense Attorneys

All instances of these three classes will have many cases.

Let’s create our three classes, initialize each with a name, and save each instance of them in an array:

Judge Class:

class Judge     attr_reader :name     @@all = []     def initialize(name)
@name = name
@@all << self
end
def self.all
@@all
end
end

Prosecutor Class:

class Prosecutor      attr_reader :name      @@all = []      def initialize(name)
@name = name
@@all << self
end
def self.all
@@all
end
end

Defense Attorney Class:

class DefenseAttorney      attr_reader :name      @@all = []      def initialize(name)
@name = name
@@all << self
end
def self.all
@@all
end
end

Great! Now we can build our database of famous individuals in the courtroom:

judy = Judge.new("Judy Sheindlin")=> #<Judge:0x00007fe8060c0058 @name="Judy Sheindlin">mccoy = Prosecutor.new("Jack McCoy")=> #<Prosecutor:0x00007fb3d591bb90 @name="Jack McCoy">atticus = DefenseAttorney.new("Atticus Finch")=> #<DefenseAttorney:0x00007fdeb28d3ec8 @name="Atticus Finch">

How can we connect these three “many” objects? Well, the goal is that we want to see these titans of fictional (and somewhat non-fictional) law meet together, which require a case for them to go up against each other.

Just as we create a “joiner” class when dealing with a many-to-many relationship, we can do the same with a many-to-many-to-many relationship.:

— — — — — — — — — — — — — — — — — — — — — — — — — —

Judge

|

Court Case (Joiner Class) — Defense Attorney

|

Prosecutor

— — — — — — — — — — — — — — — — — — — — — — — — — —

Let’s create our CourtCase class, which will initialize with an instance of a judge, an instance of a prosecutor, and an instance of a defense attorney:

CourtCase Class:

class CourtCase      attr_reader :judge, :prosecutor, :defense_attorney      @@all = []      def initialize(judge, prosecutor, defense_attorney)
@judge = judge
@prosecutor = prosecutor
@defense_attorney = defense_attorney
@@all << self
end
def self.all
@@all
end
end#---------------Creating our first CourtCase instance---------------court_case_1 = CourtCase.new(judy, mccoy, atticus)# => #<CourtCase:0x00007fedbb9aeb28#@defense_attorney=#<DefenseAttorney:0x00007fdeb28d3ec8 #@name="Atticus Finch">,#@judge=#<Judge:0x00007fe8060c0058 @name="Judy Sheindlin">,#@prosecutor=#<Prosecutor:0x00007fb3d591bb90 @name="Jack McCoy">>

Great! Now let’s create some more of each of our “many” classes and bring them of our titans together in some court cases for the ages:

#Instances of Judge, Prosecutor, and DefenseAttorney classesjoe = Judge.new("Joe Brown")annalise = DefenseAttorney.new("Annalise Keating")fielding = Prosecutor.new("Dan Fielding")niijima = Prosecutor.new("Sae Niijima")springer = Judge.new("Jerry Springer")better_call_saul = DefenseAttorney.new("Saul Goodman")#####################################################################CourtCase instancescourt_case_2 = CourtCase.new(springer, fielding, annalise)court_case_3 = CourtCase.new(judy, niijima, atticus)court_case_4 = CourtCase.new(springer, mccoy, atticus)court_case_5 = CourtCase.new(joe, mccoy, annalise)court_case_6 = CourtCase.new(joe, fielding, better_call_saul)

Excellent! And we’re able to keep track of our Super Smash Bros roster of Judges, Prosecutors, and Defense Attorneys as well as all the epic cases they’ll come together on!

We can also see all the cases each of our titans of law were involved with by adding methods to their respective classes to capture this. And, if we want to go even further, we could create methods that allow us to see cases for a particular judge in which a certain individual was the defense attorney or was the prosecutor. Our Judge class now looks like this:

class Judge      attr_reader :name      @@all = []      def initialize(name)
@name = name
@@all << self
end
def self.all
@@all
end
#Returns all of cases of a Judge instance def court_cases
CourtCase.all.select do |court_case|
court_case.judge == self
end
end
#Returns all cases of a Judge instance and a given prosecutor #instance def court_cases_with_prosecutor(prosecutor)
self.court_cases.select do |court_case|
court_case.prosecutor == prosecutor
end
end
#Returns all cases of a judge instance and a given defense attorney
#instance
def court_cases_with_defense_attorney(defense_attorney)
self.court_cases.select do |court_case|
court_case.defense_attorney == defense_attorney
end
end
end

From this, the possibilities are endless! Some ways these classes could be taken further

  • Create similar methods to the new ones above for the Prosecutor and DefenseAttorney classes to keep track of total cases as well as cases involving individuals in the other two classes.
  • Add an instance variable for each case that declares the winner of the case
  • This instance variable could then use to determine how often a judge sided with a particular individual, or how many cases either a prosecutor or defense attorney won.

The stat combos are endless! And we’re maintaining the “Single Source of Truth” principle that is essential to all of this by having our methods asking for information about other classes referencing the CourtCase class.

So, in the end, what is the Verdict on joining three objects of many instances?

It’s definitely feasible! Especially if you can keep track of maintaining that single source of truth and expounding upon the practices explored in creating a joiner class in many-to-many relationships.

Happy relationship building!

--

--