Say I have 3+ types of objects:
function Sea() {
var logs = [Logs];
this.getLog = function(ind){return logs[ind]}
}
function Log() {
var bumps = [Bumps];
this.getBump = function(ind){return bumps[ind]}
}
function Bump() {
var frogs = [Frogs];
this.getFrog = function(ind){return frogs[ind]}
}
function Frog() {
this.ribbit = function(){alert("Onomatopoeia!")}
}
So, Frog
doesn't know about Bump
, and Bump
doesn't know about Log
, but Log
can access Bump
and Frog
directly and indirectly, respectively.
Now, let's say that whenever a Frog
utters a ribbit
, the Log
moves slightly (although Frog
internally doesn't care what happens outside of itself).
Now, Mrs. Demeter suggests that I don't do this:
Sea.getLog(5).getBump(2).getFrog(1).ribbit()
It reads nice, but there's some problems. Besides the issue of tight coupling, this method also makes it impossible for the ribbit
to move the Log
unless Frog
ends up getting a reference about Log
, which means the coupling gets even tighter.
However the alternative "one dot" method looks a tad atrocious:
Sea.tellFrogOnBumpOnLogToRibbit(1, 2, 5)
A reasonable solution that has been suggested here (I think) is to create a concise but tightly coupled interface to... interface, with the more business-logicy code:
function SeaInterface() {
var sea = createSea(); //Genesis 1:10
var logInterfaces = [LogInterfaces];
this.getLog = function(ind) {return logInterfaces[ind]}
}
function LogInterface() {
var log = createLog();
var bumpInterfaces = [BumpInterfaces];
this.getBump = function(ind) {return bumpInterfaces[ind]}
//Private method that Frogs will know about internally:
var move = function(){}
}
function BumpInterface() {
var bump = createBump();
var frogInterfaces = [FrogInterfaces];
this.getFrog = function(ind) {return frogInterfaces[ind]}
}
function FrogInterface(log) {
var frog = createFrog();
this.ribbit = function() {
frog.ribbit();
log.move(); //I'm allowed to do this because I'm a simple interface
}
}
That way, I could still use chaining and make the code read well (note: chaining is not a necessity):
Sea.getLog(5).getBump(2).getFrog(1).ribbit()
And the Log
would still move after a ribbit
.
In the end, it's really just an attempt to minimize the detrimental affects that objects knowing about other objects can cause.
Questions:
Is there an equally readable or more readable and fool-proof way to achieve the same thing (multiple objects within objects where occasionally manipulating an inner object affects an outer object)?
Besides requiring more code, and the fact that the interface itself is tightly coupled (acceptable?), what other problems might arise if I were to interface with the actual object through something like this? Also... does this method have a name?
I haven't really seen many approaches like this in the wild, which is what makes me nervous.