Являются ли циклические зависимости классов плохими с точки зрения стиля кодирования? - PullRequest
17 голосов
/ 31 августа 2009

Являются ли циклические зависимости классов плохими с точки зрения стиля кодирования?

Пример:

В приложении базы данных у нас есть два класса, один инкапсулирующий информацию об одной базе данных (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);
    }
}

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

Это плохая практика, анти-паттерн или кодовый запах? Есть ли недостатки?

Ответы [ 7 ]

15 голосов
/ 31 августа 2009

В общем, я бы назвал циклические зависимости запахом кода. Обратите внимание, что термин «Запах кода» в основном указывает на то, что «этот фрагмент кода требует особого внимания и, вероятно, выиграет от изменения дизайна».

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

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

8 голосов
/ 31 августа 2009

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

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

7 голосов
/ 31 августа 2009

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

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

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

4 голосов
/ 31 августа 2009

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

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

2 голосов
/ 31 августа 2009

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

1 голос
/ 29 сентября 2009

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

Это плохо / кодовый запах?

0 голосов
/ 31 августа 2009

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

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

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

...