Как избежать круговых зависимостей здесь

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

Длинное описание:

  • Существует модуль Gui, который импортирует модуль ChessWidget;
  • ChessWidget просто обертывает модуль ChessWorld и импортирует CellButton;
  • Модуль CellButton импортирует модуль Cell;
  • Модуль ChessWorld импортирует Board (для его представления) и Players (чтобы уведомить их и получить их ходы);
  • Модуль модуля Board импортирует модуль Piece;
  • Модуль модуля Piece импортирует модуль Player;

И ЗДЕСЬ ПРОБЛЕМА:

Модуль Player должен знать о других игроках и плате, импортируя ChessWorld!

Краткое описание:

Модуль World должен знать о модуле Player (даже косвенно Board/Piece) и Player должен знать о World.

Помощь очень приветствуется.

PS: Не потому, что я не могу использовать круговые зависимости, а потому, что они злы.

Ответы

Ответ 1

Следуйте Принцип инверсии зависимостей: введите интерфейс, который ChessWorld реализует и на котором Player зависит - и/или один из которых Player реализует и на котором Piece зависит (оба или оба могут быть подходящими в зависимости от деталей о характере зависимости). Это часто сопровождается Injection of Dependency, и если зависимому нужно динамически создавать экземпляр экземпляра зависимых лиц, Factory DPs.

Ответ 2

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

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

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

ПРИМЕЧАНИЕ. Я не знаком с Java, поэтому я могу неправильно понять последствия импорта и переноса.

Но, как я понимаю, зависимости выглядят примерно так:

Gui <- ChessWidget <- ChessWorld <- CellButton <- Cell
                                 <- Board <- Piece <- Player
                                 <- Players <- ChessWorld

ИМХО проблема заключается в том, что ChessWorld выполняет слишком много разных обязанностей. Поддержание списка игроков, вероятно, лучше в отдельном модуле, таком как PlayerList, RegisteredUsers или OnlineUsers или что-то подобное. После этого рефакторинг будет изменяться следующим образом:

 Gui <- ChessWidget <- ChessWorld <- CellButton <- Cell
                                  <- Board <- Piece <- Player
                                  <- Playerlist <- Player

PlayerList - это, вероятно, то, что у вас было бы в модуле плеера. Теперь Chessworld зависит от модуля игрока, а не в другом направлении.

Я не уверен, что это соответствует вашему намерению, но мне очень интересно обсудить это, поэтому, пожалуйста, прокомментируйте.

Ответ 3

Рассмотрим, что нужно каждому объекту, а не то, что ему сейчас нужно.

Piece, вероятно, не нужно знать о игроке - о чем он должен знать, это то, что он может отправлять обновления.

Итак, для этого примера создайте интерфейс, представляющий "PieceMessageListener" или некоторые такие, и попросите Player реализовать это. Теперь обе конкреции зависят от абстракции (переход к правилу "конкреции должен зависеть от абстракций, абстракции не должны зависеть от конкреций" ).

Ответ 4

Я задержу свою руку здесь и скажу... ИМХО, вы, возможно, слишком разработали это.

Почему кусок должен иметь знания об игроке? Часть в шахматах либо черная, либо белая, независимо от того, кто контролирует (играет) ее.

Вы упомянули "модуль игрока" и "кусочный модуль" - почему они являются отдельными модулями? Почему они не просто классы данных (объекты домена) вместе в одном модуле?

Если я уже проанализировал это или не понял, как вы построили свою игру, то непременно проигнорируйте сказанное. OTOH, может быть, я правильно прочитал?