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.