2x отношения один-ко-многим в ОО - PullRequest
2 голосов
/ 19 февраля 2009

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

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

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

  • C имеет 1 B
  • B имеет 1A
  • По транзитивности, 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?

Что вы, хотя? Есть еще варианты? Комментарии? Предложения?

Ответы [ 5 ]

2 голосов
/ 19 февраля 2009

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

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

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

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

0 голосов
/ 19 февраля 2009

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

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

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

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

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

0 голосов
/ 19 февраля 2009

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

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

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

0 голосов
/ 19 февраля 2009

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

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

0 голосов
/ 19 февраля 2009

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

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

, что оставляет вариант 4

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

...