I would like to know the best way to handle a Bi-Directional association in O.O.P. I have found multiple solutions on Google and SO but each and every one of them seems to have a drawback. The language is irrelevant but let's use PHP to illustrate what i mean:
Let's say i have a simple States..[1..n]..City association:
public class State {
public $cities;
public function add_city($city) {}
}
public class City {
public $state;
public function set_state($state) {}
}
IMPLEMENTATION #1:
public class State {
public $cities;
public function add_city($city) {
$this->cities[] = $city;
$city->state = $this;
}
}
public class City {
public $state;
public function set_state($state) {
$this->state = $state;
$state->cities[] = $state;
}
}
The two problems with this implementation are:
- "$state" and "$cities" must be public (so anyone can add a city without using the public function add_city...). There is no "friend class" concept in most languages.
- the public function could have to do some operation before adding
IMPLEMENTATION #2:
public class State {
public $cities;
public function add_city($city) {
$this->cities[] = $city;
if ($city->state != $this) {
$city->set_state($this);
}
}
}
public class City {
public $state;
public function set_state($state) {
$this->state = $state;
if (!in_array($this, $state->cities)) {
$state->add_city($this);
}
}
}
A little bit better than #1 but the "set_state" function must call "in_array" which in must language is O(n) (turning a fast O(1) operation into a O(n) one.)
IMPLEMENTATION #3:
public class State {
public $cities;
public function add_city($city, $call_the_other_function = true) {
$this->cities[] = $city;
if ($call_the_other_function) {
$city->set_state($this, false);
}
}
}
public class City {
public $state;
public function set_state($state, $call_the_other_function = true) {
$this->state = $state;
if ($call_the_other_function) {
$state->add_city($this, false);
}
}
}
Implementation #3 is very effective but is kind of "ugly" (for lack of a better term) because of the extra optional parameter
Anyway, if anyone has any idea what the "Right Way"(tm) is, i would like to know.
EDIT: If that's possible i would like a solution:
- Without using another class
- Without knowing the order in which the object are created (i.e. not a "constructor" solution)