2 объекта содержат друг друга, OK?

Обсуждаем, как правильно строить приложения
Ответить
phantomlord
Сообщения: 5
Зарегистрирован: 2018.04.09, 10:08

2 объекта содержат друг друга, OK?

Сообщение phantomlord »

Всем добра! Есть вопрос. Нормально ли это, если 2 объекта будут содержать друг друга как свойства классов? Допустим, у меня есть регионы и города. Регион содержит массив объектов городов. Но город должен "знать" , к какому он относится региону. Нет ли чего плохого в том, что в классе город будет свойство класса регион? (Тот же самый объект, в массиве которого содержится этот город).
С виду все вроде нормально, т.к. в памяти это будут просто указатели. Или это плохая практика и нужен другой подход?
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2 объекта содержат друг друга, OK?

Сообщение anton_z »

Вообще в двухсторонней ассоциации как таковой ничего плохого нет.
phantomlord
Сообщения: 5
Зарегистрирован: 2018.04.09, 10:08

Re: 2 объекта содержат друг друга, OK?

Сообщение phantomlord »

Я в курсе про связи в ActiveRecord. Вопрос больше теоретический именно про "голые" классы.
Аватара пользователя
ElisDN
Сообщения: 5841
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение ElisDN »

phantomlord писал(а): 2018.04.09, 10:13 Регион содержит массив объектов городов. Но город должен "знать", к какому он относится региону.
Редко когда это бывает нужно одновременно.

В случае "добавить город в регион" мы через метод $region->addCity($city) делаем $region->cities[] = $city и в нём городу о регионе ничего знать не нужно. Если же мы выводим хлебные крошки от города, то тогда уже может пригодиться обратная связь $city->region на родителя.

В случае "добавить фотографий к региону" для $region->addPhoto($photo) требуется связь $region->photos[] = $photo. Но фотографии отдельно мы нигде не выводим, поэтому обратная связь $photo->region для фотографий не нужна.

Всё относительно. А так дочерние связи бывают нужны всегда, а связь на родителя требуется достаточно редко.
phantomlord
Сообщения: 5
Зарегистрирован: 2018.04.09, 10:08

Re: 2 объекта содержат друг друга, OK?

Сообщение phantomlord »

Спасибо за развернутый ответ!
То есть вы предлагаете свойство "родитель" в дочерних классах иметь, но заполнять по ситуации, когда это нужно?
phantomlord
Сообщения: 5
Зарегистрирован: 2018.04.09, 10:08

Re: 2 объекта содержат друг друга, OK?

Сообщение phantomlord »

Ну и связь ведь не обязательно иерархическая. Могут быть и "равноправные" сущности типа "автор" и "статья".
Аватара пользователя
ElisDN
Сообщения: 5841
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение ElisDN »

phantomlord писал(а): 2018.04.10, 21:41 То есть вы предлагаете свойство "родитель" в дочерних классах иметь, но заполнять по ситуации, когда это нужно?
Если где-то нужно, то можно оставить. Если же никому не нужно, то в нём смысла нет.
Аватара пользователя
ElisDN
Сообщения: 5841
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение ElisDN »

phantomlord писал(а): 2018.04.10, 21:47 Ну и связь ведь не обязательно иерархическая. Могут быть и "равноправные" сущности типа "автор" и "статья".
Тогда связь $article->author нужна часто, а $author->articles уже бесполезна.
phantomlord
Сообщения: 5
Зарегистрирован: 2018.04.09, 10:08

Re: 2 объекта содержат друг друга, OK?

Сообщение phantomlord »

Спасибо.
Тогда вопрос несколько трансформируется. Как, по-вашему, правильнее. Держать связи как свойства самих сущностей или управлять связями на уровне репозитория (или иного служебного слоя)?
Аватара пользователя
SiZE
Сообщения: 2813
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение SiZE »

ElisDN писал(а): 2018.04.10, 21:54 Тогда связь $article->author нужна часто, а $author->articles уже бесполезна.
Через автора можно получить его статьи, чтобы вывести в профиле автора.
Аватара пользователя
ElisDN
Сообщения: 5841
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение ElisDN »

phantomlord писал(а): 2018.04.10, 22:40 Как, по-вашему, правильнее. Держать связи как свойства самих сущностей или управлять связями на уровне репозитория (или иного служебного слоя)?
Связи нужны обычно для чего? Внутренние нужны и для логики, и для вывода на странице. Внешние равноправные связи не нужны для логики, но нужны для вывода автора статьи в представлении. А порой надо ещё и выводить отчёты со сбором данных по хитрым JOIN-ам и подзапросам. Так делать такие "запасные" связи или не делать?

В идеале лучше разбить на две подсистемы:

В сущностях Domain Model вложенные элементы агрегата присваивать в свойство вроде $this->photos. На другие агрегаты ссылаться просто по id вроде $this->authorId. То есть будут чистые агрегаты без лишних связей. Это снижает внешнюю связанность, упрощает кол и тестирование и облегчает при необходимости разбиение на микросервисы, где авторы перекочуют на отдельный сервер.

А для вывода разных выборок на сайте бывают нужны уже хитрые JOIN-ы. Например, в списке популярных пользователей нужно у каждого выводить число его статей, число комментариев и дату последнего комментария. Для этого можно сделать отдельный набор Read Model со своими репозиториями, выполняющими нужные голые SQL- или ElasticSearch-запросы и возвращающими массивы asArray() или DTO структуры со всеми необходимыми в данный момент связями и дополнительными публичными полями.

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

А в неидеальном мире, где лень делать разделение, такие запасные связи можно делать прокси-объектами для ленивой подгрузки.
Аватара пользователя
ElisDN
Сообщения: 5841
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: 2 объекта содержат друг друга, OK?

Сообщение ElisDN »

SiZE писал(а): 2018.04.11, 07:44 Через автора можно получить его статьи, чтобы вывести в профиле автора.
Это можно сделать и без автора через Post::find()->where(['author_id' => $user->id]).
anton_z
Сообщения: 483
Зарегистрирован: 2017.01.15, 15:01

Re: 2 объекта содержат друг друга, OK?

Сообщение anton_z »

ElisDN писал(а): 2018.04.11, 10:13
В идеале лучше разбить на две подсистемы:

В сущностях Domain Model вложенные элементы агрегата присваивать в свойство вроде $this->photos.

А в неидеальном мире, где лень делать разделение, такие запасные связи можно делать прокси-объектами для ленивой подгрузки.
CQRS, прокси всякие, persistence-free DomainModel, микросервисы - я бы все это воспринимал как то что можно, но далеко не всегда нужно делать. Безусловно, об этих штуках желательно знать, но применять их нужно по необходимости. Если речь идет об ассоциациях из БД, Yii relations довольно удобные и до определенных пределов их хватит с лихвой, сразу заморачиваться CQRS не стоит, потом, если(!) припрет, можно запилить. С Yii быстрее можно результат увидеть. IMHO.
es3000
Сообщения: 1
Зарегистрирован: 2019.05.21, 21:30

Re: 2 объекта содержат друг друга, OK?

Сообщение es3000 »

Здравствуйте!

Мой вопрос - очень похож на вопрос, озвученный в этой теме.
Правда среда разработки другая, но это не принципиально.
Помогите разобраться.

Есть класс "Компания" (TCompany). Этот класс содержит ссылки на класс "Банковский счет" (TAccount).
У одной компании может быть несколько банковских счетов.

Хотелось бы так реализовать эти классы, чтобы можно было написать вот такой код, использующий эти классы.
Допустим, есть две формы: первая отображает информацию о компании, вторая — отображает информацию о счете.
Хотелось бы, чтобы в этих формах можно было написать вот такой код:

В первой форме:

Код: Выделить всё

ShowInfoAboutCompany (TCompany pCompany)
{
    TextCompany = "Название компании: " + pCompany.Name;
    lAcc = pCompany.Account; // просто ссылка на счет
    TextAcc = "Номер счета: " + lAcc.Number;
}
Во второй форме:

Код: Выделить всё

ShowInfoAboutAccount (TAccount pAccount)
{
    TextAcc = "Номер счета: " + pAccount.Number;
    TextCompany = "Счет принадлежит компании: " + pAccount.Company.Name;
}
То есть, чтобы можно было:
1) из объекта-компании получить счета этой компании
2) а по объекту-счету получить компанию, которой "принадлежит" этот счет

Как лучше реализовать классы TCompany, TAccount, чтобы этот "клиентский" код работал?

Можно это реализовать "в лоб":

Код: Выделить всё

class TCompany
{
    TAccount[10] Accounts;
}

class TAccount
{
    TCompany Company;
}
И в конструкторе TCompany создавать нужное количество Accounts и передавать им ссылки на себя.

Но так наверно будет не правильно: будут циклические ссылки объектов друг на друга.
И они никогда не уничтожатся.
Так как используемый нами язык программирования для освобождения памяти имеет только простой сборщик мусора, который освобождает память объекта при уничтожении последней ссылки на объект.
Вручную управлять памятью — нет возможности.

Как правильно сделать?
Помогите пожалуйста разобраться — у меня опыта маловато.
Ответить