If I have a 2-way relationship between 2 objects, such that A relates to B and B relates to A, how can I keep this consistent such that the 2 objects will always refer back to each other?
I'm struggling to put my very simple problem into words, so here's a very simple example. I start with Husband
and Wife
:
function Husband() { this.wife; }
function Wife() { this.husband; }
var harry = new Husband();
var wendy = new Wife();
harry.wife = wendy;
wendy.husband = harry;
Logically, if Harry's wife is Wendy, then Wendy's husband is Harry.
I need a way of keeping this relationship consistent. So I create a setter method on Husband
and denote that the wife
variable should be treated as private by prefixing with an underscore.
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
wife.husband = this;
}
}
Describing this relationship is now simple and encourages consistency:
harry.setWife(wendy);
Likewise, it would be nice to have the reverse option:
wendy.setHusband(harry);
To do this I create a setHusband
method on Wife
, and tweak Husband
as needed.
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
// wife.husband = this; // <-- husband is now _husband (private)...
wife.setHusband(this); // <-- switching to a public method
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
this._husband = husband;
husband._wife = this; // <-- Oops! _wife is private!
husband.setWife(this); // <-- Oops! this results in an infinite loop!
}
}
At this point I run into a snag. My new setHusband
method needs to be able to maintain consistency, but wife
is now _wife
(private), and calling setWife
causes an infinite loop because they reciprocate each other.
I could create another set of methods like reallyJustSetHusband
but that seems silly.
My conundrum isn't particular to JavaScript, but I've mentioned it in the question in case there's a particular approach needed.
What is the best way to achieve consistency between these 2 objects? Is there something I've overlooked?
In the DOM, if you call parent.appendChild(child)
, then child.parentNode === parent
. They are never inconsistent. If a parent has a child the child has the same parent. Other relationships such as nextSibling
are also kept consistent.
A simple way is to just check for redundant values and abort early:
function Husband() {
this._wife;
this.setWife = function(wife) {
if(this._wife == wife) return; // prevents recursion
this._wife = wife;
wife.setHusband(this);
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
if(this._husband == husband) return; // prevents recursion
this._husband = husband;
husband.setWife(this);
}
}
you could also use an external state manager (redux, sql, etc) with update events, or have direct properties that don't need setters and be careful about keeping the data updated.