Сложны ли зависимости круговых классов от стиля стиля кодирования?

Сложны ли зависимости циклического класса от стиля стиля кодирования?

Пример:

В приложении базы данных есть два класса: одна инкапсуляция информации об одной базе данных (DBInfo) и один класс, который может создать соединение с базой данных. (ConnFactory)

DBInfo имеет метод getConnection, который использует ConnFactory для создания соединения. Но для этого ConnFactory нужен объект DBInfo.

Подобно этому: (Любые стили кодирования не учитываются для удобства чтения)

class DBInfo {
    String name;
    String connectionUrl;

    Connection getConnection() {
        return ConnFactory.getConnection(this);
    } 
}


class ConnFactory {
    Connection getConnection(DBInfo toWhat) {
        return new Connection(toWhat.connectionUrl);
    }
}

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

Является ли эта плохая практика, анти-шаблон или запах кода? Есть ли недостатки?

Ответы

Ответ 1

В общем, я бы назвал круговые зависимости кодовым запахом. Обратите внимание, что термин "Code Smell" в основном указывает на то, что "вот фрагмент кода, который требует особого внимания, и, вероятно, получит выгоду от редизайна".

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

В вашем примере ConnFactory кажется излишним, но это может быть потому, что ваш пример был обрезан. Мне кажется, однако, что логика создания соединения была бы лучше, если бы она была перенесена в класс DBInfo. Когда у вас уже есть класс, содержащий данные о базе данных, кажется естественным, что он несет ответственность за создание соединения с этой базой данных.

Ответ 2

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

Тем не менее, ваш код нарушает принцип единой ответственности, поскольку DBInfo не только хранит информацию о базе данных, но также отвечает за получение объектов Connection. Удалите эту конкретную функциональность отдельному классу, и все будет хорошо.

Ответ 3

Не обязательно

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

Это есть проблема, если у вас есть взаимная зависимость на уровне пакета или модуля по всем причинам, упомянутым выше и ниже.

Ответ 4

Этот код работает только в том случае, если ConnFactory.getConnection() является статическим. Лучшим решением было бы сделать getConnection() метод экземпляра ConnFactory. Тогда ваш DBInfo может принимать ConnFactory в качестве аргумента (возможно, в конструкторе, если у вас есть перегруженный конструктор). Тем не менее, я думаю, что использование статического метода для этого случая является скорее неправильной практикой, чем круговой ссылкой.

Если бы вы пошли по этому маршруту, я бы также создал интерфейс IConnFactory, с которым будет взаимодействовать DBInfo, и что ConnFactory будет реализовывать. Тогда нет круговой ссылки - как DBInfo, так и ConnFactory будет зависеть от IConnFactory, которая не зависит от них.

Ответ 5

Все, что я знаю, это то, что круговые зависимости могут стать немного проблемой, когда вы начинаете использовать платформу Injection Dependency, такую ​​как Structure Map. Большинство из этих фреймворков имеют проблемы с обработкой циклических зависимостей, иногда приводя к исключению (pardon pun:-)) Поэтому я стараюсь избегать этого, если это абсолютно необходимо, и его нельзя избежать.

Ответ 6

Как насчет двунаправленного отношения "один ко многим", которое является обычным случаем в любом приложении с использованием уровня ORM? Разве это не круговая зависимость?

Является ли это плохим/кодовым запахом?

Ответ 7

Круговые зависимости плохи, потому что:

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

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