2x отношения один-ко-многим в объектно-ориентированном режиме

StackOverflow https://stackoverflow.com/questions/563724

  •  05-09-2019
  •  | 
  •  

Вопрос

Представьте себе эти отношения:

  • 1 А имеет много Б
  • В 1 B много C...

В обратном порядке:

  • C имеет 1 B
  • Б имеет 1А
  • По транзитивности C имеет 1 A

Чтобы смоделировать эти отношения в БД, у нас есть:

TableA
a_id

TableB
b_id
a_id (fk to TableA)

TableC
c_id
b_id (fk to TableB)

Чтобы смоделировать эти отношения в ОО, у нас есть:

objA
objB
objC

И...- OBJB имеет ссылку на OBJA - OBJC имеет ссылку на OBJB

Если у objC есть метод, которому нужно вызвать метод objA, что вы будете делать?

Опция 1.

b.getA().runMethodX()

Многие люди, которых я знаю, сделали бы это, но я также узнал, что это нехорошо, потому что getA() не является поведением B в чистом смысле объектно-ориентированного подхода.Это похоже на процедурное программирование.Согласен не согласен?

Вариант 2.

Пусть objC имеет прямую ссылку на objA и objB через внедрение/установку конструктора.

Это хорошая идея?но тогда objB, на который ссылается objC, также имеет ссылку на objA.Это нормально?Или, если это не случай циклических ссылок на объекты, это приемлемо?

Вариант 3.

Переместите рассматриваемый метод в objA и передайте objC по параметру.

Я не уверен, рассматривается ли это как вариант.Я думаю, что это сработает не в каждом случае.Уменьшите метод в objC до минимума, который работает только с его состояниями, и позвольте objA делать все, что objA нужно делать до или после.

Вариант 4. (Делегация)

добавьте runMethodXinA() в B, он вызывает

a.runMethodX()

C звонки

b.runMethodXinA()

Я пробовал этот метод раньше, но, скорее всего, у B будет столько же методов, сколько у A, и не нарушает ли отсутствие одного метода как в B, так и в A DRY?

А какие у вас?Есть еще варианты?Комментарии?Предложения?

Это было полезно?

Решение

Вариант 2 представляет собой довольно опасное дублирование информации, а также любые проблемы, которые могут возникнуть из-за формы справочного графа в зависимости от вашей среды.

Вариант 3 может иметь смысл, если метод в некотором смысле зависит от типа objA, как своего рода двойная диспетчеризация.

Вариант 1 противоречит Закону Деметры, но похоже, что это наименее навязчивый вариант.

Вы также можете рассмотреть метод пересылки на objB, который передает вызов objA.

Другие советы

из вашего списка вариантов выбора является вариант 4, поскольку он подчиняется Закон Деметры

  • вариант 1 нарушает закон Деметры
  • вариант 2 вводит избыточные ссылки и косвенно нарушает Закон Деметры
  • вариант 3 загрязняет объект А знанием объекта C, что, возможно, также нарушает Закон Деметры (наоборот)

остается вариант 4

может быть вариант 5, но это выходит за рамки исходного вопроса ;-)

Я думаю, вопрос в том, какую модель вы выберете.Если вы выбираете реляционную модель, в которой вся структура известна заранее и она не изменится, вариант 1 приемлем.То есть данные/методы в A в определенной организационной схеме требуется для С.

Вариант 2 может показаться хорошей идеей.но по сути вы сохраняете две ссылки на один и тот же объект.Теперь, если B сопоставляется с другим A, B должен уведомить C, чтобы он также изменил свою ссылку.Не хорошо.

Однако вариант 4 кажется правильным для действительно объектно-ориентированного подхода.Дело в том, что A может изменить свою структуру, и вам нужно только адаптировать B — непосредственного дочернего элемента.C не знает и его не волнует, как реализовано A, достаточно того, что A просто существует, и B знает, что с ним делать.

Действительно ли вам нужны обратные указатели от C к B к A, или они у вас есть только для того, чтобы вы могли получить доступ к A из C?Возможно, А следует взять на себя ответственность за создание Б и С и за управление их отношениями.В этом сценарии A может вставить себя в C, когда он создает C (например.C может иметь конструктор, принимающий A).

Вы также можете изолировать функцию A, которая нужна C, и создать для нее интерфейс.Интерфейс (вместо A) будет передан в C при построении.Это отделит C от A.

Нет.1 или нет.4 в зависимости от обстоятельств.В моей программе для резки металла есть много случаев, когда нам нужно знать родителя/происхождение объекта.Например, у нас есть коллекция путей для фитинга, и каждый путь имеет свойство фитинга, которое возвращает фитинг, который его создал.

Однако если связанный объект существует просто потому, что он помогает исходному объекту выполнять свое предназначение (например, математическая поддержка), то делегирование, вероятно, будет лучшим подходом.

Если подключенный объект МОДИФИЦИРУЕТ исходный объект, вам, вероятно, лучше использовать шаблон посетителя.

Единственное предостережение: в большинстве ООП нужно быть осторожным с двусвязными объектами.Сборка мусора часто завершается сбоем на объектах, у которых есть связи родитель-потомок, соединяющие друг друга.Распространенной ошибкой является забыть очистить родительско-дочернюю связь.

Эту проблему можно обойти с помощью ООП, поддерживающих создание и потребление событий.С помощью прокси.Родитель передаст дочернему прокси-объект вместо себя.Когда дочернему элементу понадобится родительский объект, он получит ссылку от прокси-объекта.Прокси-объект, в свою очередь, вызывается как событие, которое заставляет родительский объект вернуться.Жестких ссылок нет, поэтому проблема, связанная с тем, что программист или сборщик мусора не выполняет очистку, смягчается.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top