Лучший способ справиться с двунаправленной ассоциацией в ООП

Я хотел бы знать, как лучше всего обращаться с двунаправленной ассоциацией в О.П. я имею нашел несколько решений в Google и SO но у каждого из них, кажется, есть недостаток. Язык не имеет значения, но давайте использовать PHP, чтобы проиллюстрировать, что я имею в виду:

Допустим, у меня простые штаты .. [1..n] .. Городская ассоциация:

public class State {
public $cities;
public function add_city($city) {}
}
public class City {
public $state;
public function set_state($state) {}
}

ОСУЩЕСТВЛЕНИЕ № 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;
}
}

Две проблемы с этой реализацией:

  • «$ state» и «$ towns» должны быть общедоступными (чтобы любой мог добавить город, не используя публичную функцию add_city …). Нет «друга»
    понятие «класс» в большинстве языков.
  • публичная функция могла бы сделать некоторую операцию перед добавлением

ОСУЩЕСТВЛЕНИЕ № 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);
}
}
}

Немного лучше, чем # 1, но функция «set_state» должна вызывать «in_array», который на языке must — O (n) (превращение быстрой операции O (1) в операцию O (n).)

ОСУЩЕСТВЛЕНИЕ № 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);
}
}
}

Реализация №3 очень эффективна, но довольно уродлива (из-за отсутствия лучшего термина) из-за дополнительного необязательного параметра

В любом случае, если у кого-то есть идея, что такое «Правильный путь», я хотел бы знать.

РЕДАКТИРОВАТЬ:
Если это возможно, я бы хотел решение:

  • Без использования другого класса
  • Не зная порядка, в котором создается объект (то есть не является «конструктором» решения)

2

Решение

Я бы попробовал использовать конструктор таким образом, когда вы устанавливаете город, вы можете напрямую передать его состояние.

public class State {
private $cities;
public function add_city($city) {
$this->cities[] = $city;
}
}

public class City {
private $state;
function __construct($state) {
$state->add_city($this)
$this->state=$state
}
}
2

Другие решения

Во всех ваших предложениях Сити знает о методах в государстве или наоборот. Что, если вы ввели третий класс, отвечающий за связь городов с государствами, например LocationService с одним методом, таким как linkCityToState? С этим вы могли бы позже расширить его linkCityToCountryили какая-то продвинутая логика, такая как getPostalCodeFromApi,

Если вы беспокоитесь о производительности, превратите свой список в хэш-набор, который снизит сложность поиска до простого O(log n),

Также в вашей реализации # 1 у вас есть city->cities…?

В любом случае, я бы никогда не выбрал решение № 3, если бы не программировал алгоритмы сжатия, драйверы, массивный запросы к базе данных и т. д.

0