Страница 1 из 1

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

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

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

Добавлено: 2018.04.10, 08:39
anton_z
Вообще в двухсторонней ассоциации как таковой ничего плохого нет.

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

Добавлено: 2018.04.10, 09:45
SiZE

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

Добавлено: 2018.04.10, 17:44
phantomlord
Я в курсе про связи в ActiveRecord. Вопрос больше теоретический именно про "голые" классы.

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

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

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

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

Всё относительно. А так дочерние связи бывают нужны всегда, а связь на родителя требуется достаточно редко.

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

Добавлено: 2018.04.10, 21:41
phantomlord
Спасибо за развернутый ответ!
То есть вы предлагаете свойство "родитель" в дочерних классах иметь, но заполнять по ситуации, когда это нужно?

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

Добавлено: 2018.04.10, 21:47
phantomlord
Ну и связь ведь не обязательно иерархическая. Могут быть и "равноправные" сущности типа "автор" и "статья".

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

Добавлено: 2018.04.10, 21:52
ElisDN
phantomlord писал(а): 2018.04.10, 21:41 То есть вы предлагаете свойство "родитель" в дочерних классах иметь, но заполнять по ситуации, когда это нужно?
Если где-то нужно, то можно оставить. Если же никому не нужно, то в нём смысла нет.

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

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

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

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

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

Добавлено: 2018.04.11, 07:44
SiZE
ElisDN писал(а): 2018.04.10, 21:54 Тогда связь $article->author нужна часто, а $author->articles уже бесполезна.
Через автора можно получить его статьи, чтобы вывести в профиле автора.

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

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

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

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

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

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

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

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

Добавлено: 2018.04.11, 10:17
ElisDN
SiZE писал(а): 2018.04.11, 07:44 Через автора можно получить его статьи, чтобы вывести в профиле автора.
Это можно сделать и без автора через Post::find()->where(['author_id' => $user->id]).

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

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

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

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

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

Добавлено: 2019.06.23, 13:02
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 и передавать им ссылки на себя.

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

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